diff --git a/.gitignore b/.gitignore
index dba5cc47..49d43a56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,78 +1,14 @@
-# built application files
-*bin\.apk
-*.ap_
-
-# files for the dex VM
-*.dex
-
-# Java class files
-*.class
-
-# generated files
-bin/
-gen/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-assets/
-
-.settings
-eclipsebin
-
-bin
-gen
-build
-out
-lib
-local.properties
-
-target
-pom.xml.*
-release.properties
-
-.idea
-*.iml
-classes
-
-obj
-
+.settings/org.eclipse.jdt.core.prefs
+*/.settings/org.eclipse.jdt.core.prefs
+**/bin/*
+**/target/*
+**/gen/*
+**/build/*
+**/.idea/*
+**/*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
.DS_Store
-
-#ignore thumbnails created by windows
-Thumbs.db
-#Ignore files build by Visual Studio
-*.obj
-*.exe
-*.pdb
-*.user
-*.aps
-*.pch
-*.vspscc
-*_i.c
-*_p.c
-*.ncb
-*.suo
-*.tlb
-*.tlh
-*.bak
-*.cache
-*.ilk
-*.log
-[Bb]in
-[Dd]ebug*/
-*.lib
-*.sbr
-obj/
-[Rr]elease*/
-_ReSharper*/
-[Tt]est[Rr]esult*
-example/.classpath
-example/.project
-example/proguard-project.txt
-*/.classpath
-*proguard-project.txt
-
-.gradle/
-gradle/*
-gradlew*
+.metadata/*
+**.hprof
diff --git a/.project b/.project
new file mode 100644
index 00000000..abc834ee
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ ListViewAnimations (Parent)
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..f578e76c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,44 @@
+language: android
+jdk: oraclejdk7
+
+env:
+ matrix:
+ - ANDROID_TARGET=15 LINT=false MAVEN=false
+ - ANDROID_TARGET=16 LINT=false MAVEN=false
+ - ANDROID_TARGET=17 LINT=false MAVEN=false
+ - ANDROID_TARGET=18 LINT=false MAVEN=false
+ - ANDROID_TARGET=19 LINT=true MAVEN=true
+ global:
+ - TERM=dumb
+ - COMPONENTS=build-tools-20.0.0,android-20,extra-android-m2repository,extra-android-support,extra-google-m2repository
+ - LICENSES="android-sdk-license-5be876d5|android-sdk-preview-license-52d11cd2"
+ - secure: Fo/btrr+HpwmQL6TNxZuh/TWRMouz9Z1RCo77tf3crNP4OHq9EAkduLc/b4i6HCGFM3fyfJ7AM5XMa3OQvXfF84FQnew7NGE6zB/CKFfKBIZyzntvrDg4VqWEAHzunT6+eBnGtxtrAE0bBo4MmUCklXWP8j5UAyjfFpSeJdOywY=
+ - secure: s56rqSiiwX40CQWeHTcbyVLRtwLP/pcHWqaVCKuXtZ/m9SgRI/SHp9o8ITI2dzr3syEeAkHjkJOkHQSpH0bHi1xKY4M9ODcCugwv+yDEELw1qmI6Cvwjvj1cKsG7ebt39OnCkz+TqhtI/iOEkxq7mTizFjNIxIImMkKxeQwcFgE=
+ - secure: UOQ9keLBuJw5Ed/eYtXarwoGuJSSvgGTob+D9aU8hVRoTUXpSDuHtk9TdS4MNXQ33jgFZEsmkpwRmV54XVEGe6xcri1uys1JDzjc+SqTEgbEh1QktfIWGvOx49YZtI2Zei0324S2qXRtemKMw4N+1XRRMP/IXEogr26Xi5JLVcI=
+
+before_install:
+ - curl -3L https://raw.github.com/embarkmobile/android-sdk-installer/version-2/android-sdk-installer | bash /dev/stdin --install=$COMPONENTS,android-$ANDROID_TARGET,sys-img-armeabi-v7a-android-$ANDROID_TARGET --accept=$LICENSES && source ~/.android-sdk-installer/env
+ - curl -3L -o wait_for_emulator https://github.com/embarkmobile/android-sdk-installer/raw/version-2/wait_for_emulator
+ - sudo chmod +x wait_for_emulator
+
+ # Start the emulator
+ - echo no | android create avd --force -n test -t android-$ANDROID_TARGET --abi armeabi-v7a
+ - emulator -avd test -no-audio -no-window &
+
+before_script:
+ # Downloads gradle stuff
+ - gradle
+ - ./wait_for_emulator
+
+install:
+ - true
+
+script:
+ - gradle connectedCheck --info
+ - if [[ $LINT == 'true' ]]; then gradle lint ; fi
+
+after_success:
+ - if [[ $MAVEN == 'true' && $TRAVIS_BRANCH == 'dev' && $TRAVIS_PULL_REQUEST == 'false' ]]; then gradle uploadArchives -PnexusUsername="${nexusUsername}" -PnexusPassword="${nexusPassword}" ; fi
+
+notifications:
+ email: false
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..30803187
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,35 @@
+# Reporting issues
+
+Found a bug in the library? Don't hesitate to open an issue! But first:
+
+* Check if it hasn't been reported yet.
+* Verify the bug still exists in the `dev` branch.
+
+When describing an issue, be precise:
+
+* Include steps to reproduce (including sample code if necessary);
+* What happened? What do you think should have happened?
+* On what Android version does this issue occur?
+
+# Contributing code
+
+## Please do!
+
+I'm happy to view and accept pull requests. However, it is important to follow these guidelines if you want to contribute.
+
+## General steps
+
+* [Fork](https://github.com/nhaarman/ListViewAnimations/fork) the repository.
+* Create a local clone of the repository.
+* Create a local branch, **based on the `dev` branch** (see the *Rules* section)
+* Commit your code, and push the changes to your own repository.
+* Create a pull request, specifying that you want to merge into the `dev` branch (or any child branch of it)
+
+## Rules
+
+* Branch names should start with either `feature_` or `bugfix_`. If there is an open issue, include its number, like `bugfix_123`.
+* **Do not** include in your commit message anything related to automatic issue closing, such as `Fixes issue 123`. We'll do that when merging your pull request.
+* **Do not** put any `@author` comment. Git keeps track of all your changes and `@author` does more harm than good.
+* **Do not** issue a pull request into the `master` branch.
+* Try to keep the diff as small as possible. For example, be aware of auto formatting.
+* All files should have the Apache 2 License header.
\ No newline at end of file
diff --git a/ListViewAnimationsExample.apk b/ListViewAnimationsExample.apk
deleted file mode 100644
index ae38cba2..00000000
Binary files a/ListViewAnimationsExample.apk and /dev/null differ
diff --git a/README.md b/README.md
index ecb8e1de..bbb80f2e 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,33 @@
-ListViewAnimations ([Play Store Demo][1])
+ListViewAnimations ([Play Store Demo][1]) [![Build Status](https://travis-ci.org/nhaarman/ListViewAnimations.svg)](https://travis-ci.org/nhaarman/ListViewAnimations)
===========
ListViewAnimations is an Open Source Android library that allows developers to easily create ListViews with animations.
Feel free to use it all you want in your Android apps provided that you cite this project and include the license in your app.
-Known applications using ListViewAnimations
------
-[Ultimate Tic-Tac-Toe][12] | [Light Flow Lite - LED Control][18] | [TreinVerkeer][6] | [Pearl Jam Lyrics][19] | [Calorie Chart][20] | [Car Hire][10] | [Super BART][11] | [DK FlashCards][15] | [Counter Plus][22] | [SimpleNews - RSS Reader][23] | [Voorlees Verhaaltjes 2.0][21] | [Per-App Modes][26] | [SimpleNews][27]
-
Features
-----
ListViewAnimations provides the following features:
-* Appearance animations for items in ListViews, GridViews, and other AbsListViews;
- * Built in animations include Alpha, SwingRightIn, SwingLeftIn, SwingBottomIn, SwingRightIn and ScaleIn.
+* Appearance animations for items in `ListViews`, `GridViews`, other `AbsListViews`;
+ * Built in animations include `Alpha`, `SwingRightIn`, `SwingLeftIn`, `SwingBottomIn`, `SwingRightIn` and `ScaleIn`.
* Other animations can easily be added
-* Swipe-to-Dismiss, Swipe-To-Dismiss with contextual undo (and optionally count down);
+ * StickyListHeaders is supported, other implementations can easily be added.
+* Swipe-to-Dismiss, Swipe-To-Dismiss with contextual undo;
* Drag-and-Drop reordering;
-* Animate dismissal of items;
* Animate addition of items;
* Smoothly expand your items to reveal more content;
+![](https://raw.githubusercontent.com/nhaarman/ListViewAnimations/gh-pages/images/dynamiclistview.gif "DynamicListView")
+
Setup
-----
-* In Eclipse, just import the library as an Android library project.
-* Project > Clean to generate the binaries you need, like R.java, etc.
-* Then, just add ListViewAnimations as a dependency to your existing project and you're good to go!
-**Or**:
+The library consists of separate modules:
-* [Download the .jar file][4]
-* [Download the latest NineOldAndroids .jar file][17]
-* Add the .jar files to your project's `libs` folder, or add them as external jars to your project's build path.
+* `lib-core`: The core of the library, and contains appearance animations.
+* `lib-manipulation`: Contains the item manipulation options, such as Swipe-to-Dismiss, and Drag-and-Drop.
+* `lib-core-slh`: An extension of `lib-core` to support `StickyListHeaders`.
-**Or**:
+When using `lib-manipulation` or `lib-core-slh`, `lib-core` is included as well.
Add the following to your `build.gradle`:
@@ -40,44 +35,72 @@ Add the following to your `build.gradle`:
mavenCentral()
}
- dependencies{
- compile 'com.nhaarman.listviewanimations:library:2.6.0'
+ dependencies {
+ compile 'com.nhaarman.listviewanimations:lib-core:3.0.+'
+ compile 'com.nhaarman.listviewanimations:lib-manipulation:3.0.+'
+ compile 'com.nhaarman.listviewanimations:lib-core-slh:3.0.+'
}
**Or**:
+* Download the jar files you need:
+ * [`lib-core`][8]
+ * [`lib-manipulation`][9]
+ * [`lib-core-slh`][10]
+* [Download the latest NineOldAndroids .jar file][6]
+* Add the .jar files to your project's `libs` folder, or add them as external jars to your project's build path.
+
+**Or**:
+
Add the following to your `pom.xml`:
com.nhaarman.listviewanimations
- library
- 2.6.0
+ lib-core
+ 3.0.0
-
-Usage
+
+ com.nhaarman.listviewanimations
+ lib-manipulation
+ 3.0.0
+
+
+ com.nhaarman.listviewanimations
+ lib-core-slh
+ 3.0.0
+
+
+
+Getting Started
-----
-Please refer to the [Wiki][13] pages to learn more about how to use this library.
+
+* [Wiki][11]: Tutorials and examples
+* Docs:
+ * `lib-core` - [Javadoc][12]
+ * `lib-manipulation` - [Javadoc][13]
+ * `lib-core-slh` - [Javadoc][14]
Contribute
-----
Please do! I'm happy to review and accept pull requests.
-Please read the [Contributing wiki](https://github.com/nhaarman/ListViewAnimations/wiki/Contributing) before you do.
+Please read [Contributing](https://github.com/nhaarman/ListViewAnimations/blob/master/CONTRIBUTING.md) before you do.
Developed By
-----
* Niek Haarman
+***
+
Special Thanks
-----
-* Roman Nurik - The ListViewAnimations library uses a modified version of his [SwipeDismissListViewTouchListener][5] to support swipe-to-dismiss.
-* DevBytes - Drag-and-Drop reordering is done by a modified version of their [DynamicListView][16].
+* DevBytes - Drag-and-Drop reordering is done by a rewritten version of their [DynamicListView][5].
* Jake Warthon - To support devices pre-HC (<3.0), a copy of [NineOldAndroids][2] is included.
-* [Contributors][25]
+* [Contributors][7]
License
-----
- Copyright 2013 Niek Haarman
+ Copyright 2014 Niek Haarman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -94,26 +117,13 @@ License
[1]: https://play.google.com/store/apps/details?id=com.haarman.listviewanimations
[2]: http://nineoldandroids.com/
[3]: http://en.wikipedia.org/wiki/Decorator_pattern
- [4]: https://github.com/nhaarman/ListViewAnimations/blob/master/com.haarman.listviewanimations-2.6.0.jar?raw=true
- [5]: https://gist.github.com/romannurik/2980593
- [6]: https://play.google.com/store/apps/details?id=com.haarman.treinverkeer
- [7]: https://www.twitter.com/niekfct
- [8]: https://plus.google.com/106017817931984343451
- [9]: https://play.google.com/store/apps/details?id=com.niek.runningapp
- [10]: https://play.google.com/store/apps/details?id=com.rentalcars.handset
- [11]: https://play.google.com/store/apps/details?id=com.getgoodcode.bart
- [12]: https://play.google.com/store/apps/details?id=com.haarman.ultimatettt
- [13]: https://github.com/nhaarman/ListViewAnimations/wiki
- [15]: https://play.google.com/store/apps/details?id=com.ducky.flashcards
- [16]: http://youtu.be/_BZIvjMgH-Q
- [17]: https://github.com/JakeWharton/NineOldAndroids/downloads
- [18]: https://play.google.com/store/apps/details?id=com.rageconsulting.android.lightflowlite
- [19]: https://play.google.com/store/apps/details?id=com.juannale.pearljamlyricsapp
- [20]: https://play.google.com/store/apps/details?id=com.cafetaso.foodinfo
- [21]: https://play.google.com/store/apps/details?id=sa.voorleesVerhaaltjes
- [22]: https://play.google.com/store/apps/details?id=com.seedform.counter
- [23]: https://play.google.com/store/apps/details?id=de.dala.simplenews
- [24]: https://github.com/Dalanie/SimpleNews
- [25]: https://github.com/nhaarman/ListViewAnimations/graphs/contributors
- [26]: https://play.google.com/store/apps/details?id=com.franco.perappmodes
- [27]: https://play.google.com/store/apps/details?id=de.dala.simplenews
+ [5]: http://youtu.be/_BZIvjMgH-Q
+ [6]: https://github.com/JakeWharton/NineOldAndroids/downloads
+ [7]: https://github.com/nhaarman/ListViewAnimations/graphs/contributors
+ [8]: https://github.com/nhaarman/ListViewAnimations/releases/download/3.0.0/listviewanimations_lib-core_3.0.0.jar
+ [9]: https://github.com/nhaarman/ListViewAnimations/releases/download/3.0.0/listviewanimations_lib-manipulation_3.0.0.jar
+ [10]: https://github.com/nhaarman/ListViewAnimations/releases/download/3.0.0/listviewanimations_lib-core-slh_3.0.0.jar
+ [11]: https://github.com/nhaarman/ListViewAnimations/wiki
+ [12]: http://nhaarman.github.io/ListViewAnimations/javadoc/3.0.0/lib-core
+ [13]: http://nhaarman.github.io/ListViewAnimations/javadoc/3.0.0/lib-manipulation
+ [14]: http://nhaarman.github.io/ListViewAnimations/javadoc/3.0.0/lib-core-slh
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 00000000..cf0bee00
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,16 @@
+ListViewAnimations Release Process
+=================================
+
+ 1. Make sure it builds!
+
+ gradlew clean build
+
+ 2. Check the version number in the root `gradle.properties`.
+ 3. Make the release!
+
+ gradlew clean generateReleaseJavadoc pushMaven -DisRelease=true
+
+ 4. Promote the Maven artifact on Sonatype's OSS Nexus install.
+ 5. Tag commit as release (`x.x.x`)
+ 6. Provide changelog / `.jar` files at [the Releases page](https://github.com/nhaarman/ListViewAnimations/releases).
+ 7. Increment the patch version number for future SNAPSHOT releases in the root `gradle.properties`.
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 0fe6e91c..f2bfa322 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,18 @@
-import java.util.regex.Pattern
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
buildscript {
repositories {
@@ -6,15 +20,11 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.8.+'
+ classpath 'com.android.tools.build:gradle:0.12.+'
+ classpath 'org.ajoberstar:gradle-git:0.9.+'
}
}
-def isReleaseBuild() {
- println version
- return version.contains("SNAPSHOT") == false
-}
-
allprojects {
group = 'com.nhaarman.listviewanimations'
version = VERSION_NAME
@@ -22,66 +32,4 @@ allprojects {
repositories {
mavenCentral()
}
-
- tasks.withType(Compile) {
- options.encoding = 'UTF-8'
- }
-}
-
-
-task('increaseVersionCode') << {
- def files = new File[2];
- files[0] = file("library/AndroidManifest.xml")
- files[1] = file("example/AndroidManifest.xml")
- def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
- for (int i = 0; i < files.length; ++i) {
- def manifestText = files[i].getText()
- def matcher = pattern.matcher(manifestText)
- matcher.find()
- def currentVersionCode = Long.parseLong(matcher.group(1));
- def versionCode = Long.parseLong(new Date().format('yyyyMMdd') + "1")
- while (versionCode <= currentVersionCode) {
- ++versionCode
- }
- println(versionCode)
- def manifestContent = matcher.replaceAll("versionCode=\"" + versionCode + "\"")
- files[i].write(manifestContent)
- }
-}
-
-task('syncVersionName') << {
- def manifestFiles = new File[2];
- manifestFiles[0] = file("library/AndroidManifest.xml")
- manifestFiles[1] = file("example/AndroidManifest.xml")
- def pattern = Pattern.compile("versionName=\"(.+)\"")
- for (int i = 0; i < manifestFiles.length; ++i) {
- def manifestText = manifestFiles[i].getText()
- def matcher = pattern.matcher(manifestText)
- matcher.find()
- def manifestContent = matcher.replaceAll("versionName=\"" + version + "\"")
- manifestFiles[i].write(manifestContent)
- }
-
- def readmeFile = file("README.md")
- def readmeText = readmeFile.getText()
- def matcher = pattern.matcher(readmeText)
- matcher.find()
- def previousVersion = matcher.group(1)
- def readmeContent = readmeText.replaceAll(previousVersion, version)
-
- pattern = Pattern.compile("repostart -->(.+)" + repo + "
+
-
-
+
\ No newline at end of file
diff --git a/example/pom.xml b/example/pom.xml
index 3c378776..825a9d52 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -1,147 +1,222 @@
-
- 4.0.0
+
-
- com.haarman.listviewanimations
- parent
- 1.0.0-SNAPSHOT
-
+
+ 4.0.0
- example
- apk
+
+ com.nhaarman.listviewanimations
+ parent
+ 3.0.0-SNAPSHOT
+
- ListViewAnimations (Example)
+ example
+ apk
-
-
-
- com.google.android
- android
- provided
-
-
- com.google.android
- support-v4
-
+ ListViewAnimations (Example)
-
-
- com.haarman.listviewanimations
- listviewanimations
- ${project.version}
- apklib
-
+
+
+
+ com.google.android
+ android
+ provided
+
+
+ com.google.android
+ support-v4
+
-
-
- junit
- junit
- test
-
-
+
+
+ com.nhaarman.listviewanimations
+ lib-core-slh
+ ${project.version}
+ apklib
+
+
+ com.nhaarman.listviewanimations
+ lib-manipulation
+ ${project.version}
+ apklib
+
+
-
- src
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- true
-
-
- runLint
- compile
-
- lint
-
-
-
-
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ ${project.basedir}/src/main/AndroidManifest.xml
+ ${project.basedir}/src/main/res
+ ${project.basedir}/src/main/aidl
+
+
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- package
-
- attach-artifact
-
-
-
-
- jar
- ${project.build.directory}/${project.build.finalName}.jar
-
-
-
-
-
-
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ package
+ attach-artifact
+
+ attach-artifact
+
+
+
+
+ jar
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+
+
+
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- attach-javadocs
-
- jar
-
-
- true
-
-
-
-
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
-
- org.apache.maven.plugins
- maven-release-plugin
-
-
-
+
+ org.apache.maven.plugins
+ maven-release-plugin
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+
+ android-maven-plugin
+ [3.8.2,)
+
+ consume-aar
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- release
-
-
- performRelease
- true
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- ${maven-gpg-plugin.version}
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
+
+
+ release
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+ verify
+
+ package
+ true
+
+
+ ${project.build.directory}/${project.artifactId}.apk
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ true
+
+ false
+
+
+ false
+ true
+ ${project.build.directory}/${project.artifactId}-signed-aligned.apk
+
+
+ false
+
+
+
+
+ alignApk
+ package
+
+ zipalign
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/proguard-project.txt b/example/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/example/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/example/project.properties b/example/project.properties
index de78aecc..e5c05e2e 100644
--- a/example/project.properties
+++ b/example/project.properties
@@ -1,3 +1,19 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
@@ -12,5 +28,3 @@
# Project target.
target=android-19
-android.library.reference.1=..\\..\\android-support-v7-appcompat
-android.library.reference.2=../library
diff --git a/example/res/drawable-xhdpi/ic_beer.png b/example/res/drawable-xhdpi/ic_beer.png
deleted file mode 100644
index 904d4c6c..00000000
Binary files a/example/res/drawable-xhdpi/ic_beer.png and /dev/null differ
diff --git a/example/res/drawable-xhdpi/ic_github.png b/example/res/drawable-xhdpi/ic_github.png
deleted file mode 100644
index 2e8a7c29..00000000
Binary files a/example/res/drawable-xhdpi/ic_github.png and /dev/null differ
diff --git a/example/res/drawable-xxhdpi/ic_beer.png b/example/res/drawable-xxhdpi/ic_beer.png
deleted file mode 100644
index cc60b9de..00000000
Binary files a/example/res/drawable-xxhdpi/ic_beer.png and /dev/null differ
diff --git a/example/res/layout/activity_animateremoval.xml b/example/res/layout/activity_animateremoval.xml
deleted file mode 100644
index 4ca3375b..00000000
--- a/example/res/layout/activity_animateremoval.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_animateremoval_row.xml b/example/res/layout/activity_animateremoval_row.xml
deleted file mode 100644
index 27350fa8..00000000
--- a/example/res/layout/activity_animateremoval_row.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
diff --git a/example/res/layout/activity_draganddrop.xml b/example/res/layout/activity_draganddrop.xml
deleted file mode 100644
index 0aace78c..00000000
--- a/example/res/layout/activity_draganddrop.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_examples_itemmanipulations.xml b/example/res/layout/activity_examples_itemmanipulations.xml
deleted file mode 100644
index 50832c31..00000000
--- a/example/res/layout/activity_examples_itemmanipulations.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_expandablelistitem_card.xml b/example/res/layout/activity_expandablelistitem_card.xml
deleted file mode 100644
index 60f80aa8..00000000
--- a/example/res/layout/activity_expandablelistitem_card.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_googlecards.xml b/example/res/layout/activity_googlecards.xml
deleted file mode 100644
index dbaffd53..00000000
--- a/example/res/layout/activity_googlecards.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/example/res/layout/activity_googlecards_card.xml b/example/res/layout/activity_googlecards_card.xml
deleted file mode 100644
index 03fda618..00000000
--- a/example/res/layout/activity_googlecards_card.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_gridview.xml b/example/res/layout/activity_gridview.xml
deleted file mode 100644
index 8ba03796..00000000
--- a/example/res/layout/activity_gridview.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_main.xml b/example/res/layout/activity_main.xml
deleted file mode 100644
index cbb65d5b..00000000
--- a/example/res/layout/activity_main.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/layout/activity_mylist.xml b/example/res/layout/activity_mylist.xml
deleted file mode 100644
index 5c9a9b93..00000000
--- a/example/res/layout/activity_mylist.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/res/menu/menu_expandablelistitem.xml b/example/res/menu/menu_expandablelistitem.xml
deleted file mode 100644
index 6e02eb78..00000000
--- a/example/res/menu/menu_expandablelistitem.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
\ No newline at end of file
diff --git a/example/res/menu/menu_main.xml b/example/res/menu/menu_main.xml
deleted file mode 100644
index 33fa8ac2..00000000
--- a/example/res/menu/menu_main.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
\ No newline at end of file
diff --git a/example/res/values-v19/dimens.xml b/example/res/values-v19/dimens.xml
deleted file mode 100644
index 55e49d7c..00000000
--- a/example/res/values-v19/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 73dp
-
-
\ No newline at end of file
diff --git a/example/res/values/dimens.xml b/example/res/values/dimens.xml
deleted file mode 100644
index 07f1f44c..00000000
--- a/example/res/values/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 0dp
-
-
\ No newline at end of file
diff --git a/example/res/values/strings.xml b/example/res/values/strings.xml
deleted file mode 100644
index df1f4b18..00000000
--- a/example/res/values/strings.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
- ListViewAnimationsExample
- Buy me a beer!
- Buy me a beer!
- Buy me 2 beers!
- Buy me 5 beers!
- Buy me 10 beers!
- Github
- Google cards example
- GridView example
- Appearance Animation
- Limit to 2
- Item Manipulation
- GridView example
- Drag and Drop
- Swipe to dismiss
- Animate dismiss
- Animate addition
- Remove selected
- Expand list items
- Item deleted
- Tap to undo
- Tap on cards to expand or collapse them
- This is card number %d
-
-
- Dismissing in 1...
- Dismissing in %d...
-
-
- Dismissing
-
-
\ No newline at end of file
diff --git a/example/res/values/styles.xml b/example/res/values/styles.xml
deleted file mode 100644
index 8bf43d37..00000000
--- a/example/res/values/styles.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/example/src/com/haarman/listviewanimations/BaseActivity.java b/example/src/com/haarman/listviewanimations/BaseActivity.java
deleted file mode 100644
index 42d688e7..00000000
--- a/example/src/com/haarman/listviewanimations/BaseActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.haarman.listviewanimations;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
-import android.view.MenuItem;
-
-public class BaseActivity extends ActionBarActivity {
-
- @SuppressLint("InlinedApi")
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- }
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/GoogleCardsActivity.java b/example/src/com/haarman/listviewanimations/GoogleCardsActivity.java
deleted file mode 100644
index 42567a3a..00000000
--- a/example/src/com/haarman/listviewanimations/GoogleCardsActivity.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.support.v4.util.LruCache;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.SwingBottomInAnimationAdapter;
-
-public class GoogleCardsActivity extends BaseActivity implements OnDismissCallback {
-
- private GoogleCardsAdapter mGoogleCardsAdapter;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_googlecards);
-
- ListView listView = (ListView) findViewById(R.id.activity_googlecards_listview);
-
- mGoogleCardsAdapter = new GoogleCardsAdapter(this);
- SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(new SwipeDismissAdapter(mGoogleCardsAdapter, this));
- swingBottomInAnimationAdapter.setInitialDelayMillis(300);
- swingBottomInAnimationAdapter.setAbsListView(listView);
-
- listView.setAdapter(swingBottomInAnimationAdapter);
-
- mGoogleCardsAdapter.addAll(getItems());
- }
-
- private ArrayList getItems() {
- ArrayList items = new ArrayList();
- for (int i = 0; i < 100; i++) {
- items.add(i);
- }
- return items;
- }
-
- @Override
- public void onDismiss(final AbsListView listView, final int[] reverseSortedPositions) {
- for (int position : reverseSortedPositions) {
- mGoogleCardsAdapter.remove(position);
- }
- }
-
- private static class GoogleCardsAdapter extends ArrayAdapter {
-
- private final Context mContext;
- private final LruCache mMemoryCache;
-
- public GoogleCardsAdapter(final Context context) {
- mContext = context;
-
- final int cacheSize = (int) (Runtime.getRuntime().maxMemory() / 1024);
- mMemoryCache = new LruCache(cacheSize) {
- @Override
- protected int sizeOf(final Integer key, final Bitmap bitmap) {
- // The cache size will be measured in kilobytes rather than
- // number of items.
- return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
- }
- };
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- ViewHolder viewHolder;
- View view = convertView;
- if (view == null) {
- view = LayoutInflater.from(mContext).inflate(R.layout.activity_googlecards_card, parent, false);
-
- viewHolder = new ViewHolder();
- viewHolder.textView = (TextView) view.findViewById(R.id.activity_googlecards_card_textview);
- view.setTag(viewHolder);
-
- viewHolder.imageView = (ImageView) view.findViewById(R.id.activity_googlecards_card_imageview);
- } else {
- viewHolder = (ViewHolder) view.getTag();
- }
-
- viewHolder.textView.setText("This is card " + (getItem(position) + 1));
- setImageView(viewHolder, position);
-
- return view;
- }
-
- private void setImageView(final ViewHolder viewHolder, final int position) {
- int imageResId;
- switch (getItem(position) % 5) {
- case 0:
- imageResId = R.drawable.img_nature1;
- break;
- case 1:
- imageResId = R.drawable.img_nature2;
- break;
- case 2:
- imageResId = R.drawable.img_nature3;
- break;
- case 3:
- imageResId = R.drawable.img_nature4;
- break;
- default:
- imageResId = R.drawable.img_nature5;
- }
-
- Bitmap bitmap = getBitmapFromMemCache(imageResId);
- if (bitmap == null) {
- bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
- addBitmapToMemoryCache(imageResId, bitmap);
- }
- viewHolder.imageView.setImageBitmap(bitmap);
- }
-
- private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
- if (getBitmapFromMemCache(key) == null) {
- mMemoryCache.put(key, bitmap);
- }
- }
-
- private Bitmap getBitmapFromMemCache(final int key) {
- return mMemoryCache.get(key);
- }
-
- private static class ViewHolder {
- TextView textView;
- ImageView imageView;
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/GridViewActivity.java b/example/src/com/haarman/listviewanimations/GridViewActivity.java
deleted file mode 100644
index c5ecbbbc..00000000
--- a/example/src/com/haarman/listviewanimations/GridViewActivity.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package com.haarman.listviewanimations;
-
-import java.util.ArrayList;
-import java.util.List;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v4.util.LruCache;
-import android.support.v7.app.ActionBarActivity;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.GridView;
-import android.widget.ImageView;
-
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.SwingBottomInAnimationAdapter;
-
-public class GridViewActivity extends ActionBarActivity {
-
- @SuppressLint("InlinedApi")
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- }
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_gridview);
-
- GridView gridView = (GridView) findViewById(R.id.activity_gridview_gv);
- SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(new MyAdapter(this, getItems()));
- swingBottomInAnimationAdapter.setAbsListView(gridView);
- swingBottomInAnimationAdapter.setInitialDelayMillis(300);
- gridView.setAdapter(swingBottomInAnimationAdapter);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
- private ArrayList getItems() {
- ArrayList items = new ArrayList();
- for (int i = 0; i < 100; i++) {
- items.add(i);
- }
- return items;
- }
-
- private static class MyAdapter extends ArrayAdapter {
-
- private final Context mContext;
- private final LruCache mMemoryCache;
-
- public MyAdapter(final Context context, final List list) {
- super(list);
- mContext = context;
-
- final int cacheSize = (int) (Runtime.getRuntime().maxMemory() / 1024);
- mMemoryCache = new LruCache(cacheSize) {
- @Override
- protected int sizeOf(final Integer key, final Bitmap bitmap) {
- // The cache size will be measured in kilobytes rather than
- // number of items.
- return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
- }
- };
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup viewGroup) {
- ImageView imageView = (ImageView) convertView;
-
- if (imageView == null) {
- imageView = new ImageView(mContext);
- imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
- }
-
- int imageResId;
- switch (getItem(position) % 5) {
- case 0:
- imageResId = R.drawable.img_nature1;
- break;
- case 1:
- imageResId = R.drawable.img_nature2;
- break;
- case 2:
- imageResId = R.drawable.img_nature3;
- break;
- case 3:
- imageResId = R.drawable.img_nature4;
- break;
- default:
- imageResId = R.drawable.img_nature5;
- }
-
- Bitmap bitmap = getBitmapFromMemCache(imageResId);
- if (bitmap == null) {
- bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
- addBitmapToMemoryCache(imageResId, bitmap);
- }
- imageView.setImageBitmap(bitmap);
-
- return imageView;
- }
-
- private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
- if (getBitmapFromMemCache(key) == null) {
- mMemoryCache.put(key, bitmap);
- }
- }
-
- private Bitmap getBitmapFromMemCache(final int key) {
- return mMemoryCache.get(key);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/MainActivity.java b/example/src/com/haarman/listviewanimations/MainActivity.java
deleted file mode 100644
index 32000817..00000000
--- a/example/src/com/haarman/listviewanimations/MainActivity.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations;
-
-import java.util.ArrayList;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender.SendIntentException;
-import android.content.ServiceConnection;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Toast;
-
-import com.android.vending.billing.IInAppBillingService;
-import com.haarman.listviewanimations.appearanceexamples.AppearanceExamplesActivity;
-import com.haarman.listviewanimations.itemmanipulationexamples.ItemManipulationsExamplesActivity;
-
-public class MainActivity extends Activity {
-
- @SuppressLint("InlinedApi")
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- }
-
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), mServiceConn, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.menu_main, menu);
-
- menu.findItem(R.id.menu_main_donate).setVisible(mService != null);
-
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_main_github:
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("http://nhaarman.github.io/ListViewAnimations?ref=app"));
- startActivity(intent);
- return true;
- case R.id.menu_main_beer:
- buy("beer");
- return true;
- case R.id.menu_main_beer2:
- buy("beer2");
- return true;
- case R.id.menu_main_beer3:
- buy("beer3");
- return true;
- case R.id.menu_main_beer4:
- buy("beer4");
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- public void onGoogleCardsExampleClicked(final View view) {
- Intent intent = new Intent(this, GoogleCardsActivity.class);
- startActivity(intent);
- }
-
- public void onGridViewExampleClicked(final View view) {
- Intent intent = new Intent(this, GridViewActivity.class);
- startActivity(intent);
- }
-
- public void onAppearanceClicked(final View view) {
- Intent intent = new Intent(this, AppearanceExamplesActivity.class);
- startActivity(intent);
- }
-
- public void onItemManipulationClicked(final View view) {
- Intent intent = new Intent(this, ItemManipulationsExamplesActivity.class);
- startActivity(intent);
- }
-
- private IInAppBillingService mService;
-
- private final ServiceConnection mServiceConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(final ComponentName name) {
- mService = null;
- // supportInvalidateOptionsMenu();
- }
-
- @Override
- public void onServiceConnected(final ComponentName name, final IBinder service) {
- mService = IInAppBillingService.Stub.asInterface(service);
- // supportInvalidateOptionsMenu();
-
- new Thread() {
-
- @Override
- public void run() {
- try {
- Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
-
- int response = ownedItems.getInt("RESPONSE_CODE");
- if (response == 0) {
- ArrayList purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
-
- if (purchaseDataList != null) {
- for (String purchaseData : purchaseDataList) {
- JSONObject json = new JSONObject(purchaseData);
- mService.consumePurchase(3, getPackageName(), json.getString("purchaseToken"));
- }
- }
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- };
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mServiceConn != null) {
- unbindService(mServiceConn);
- }
- }
-
- @Override
- protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (resultCode == RESULT_OK) {
- Toast.makeText(this, "Thank you!", Toast.LENGTH_LONG).show();
-
- new Thread() {
-
- @Override
- public void run() {
- try {
- JSONObject json = new JSONObject(data.getStringExtra("INAPP_PURCHASE_DATA"));
- mService.consumePurchase(3, getPackageName(), json.getString("purchaseToken"));
- } catch (JSONException e) {
- e.printStackTrace();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- }.start();
- }
- }
-
- private void buy(final String sku) {
- try {
- Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", "bGoa+V7g/ysDXvKwqq+JTFn4uQZbPiQJo4pf9RzJ");
- PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
- if (pendingIntent != null) {
- startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), 0, 0, 0);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (SendIntentException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/MyListActivity.java b/example/src/com/haarman/listviewanimations/MyListActivity.java
deleted file mode 100644
index 50311b86..00000000
--- a/example/src/com/haarman/listviewanimations/MyListActivity.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.nhaarman.listviewanimations.ArrayAdapter;
-
-public class MyListActivity extends BaseActivity {
-
- private ListView mListView;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_mylist);
- mListView = (ListView) findViewById(R.id.activity_mylist_listview);
- mListView.setDivider(null);
- }
-
- public ListView getListView() {
- return mListView;
- }
-
- protected ArrayAdapter createListAdapter() {
- return new MyListAdapter(this, getItems());
- }
-
- public static ArrayList getItems() {
- ArrayList items = new ArrayList();
- for (int i = 0; i < 1000; i++) {
- items.add(i);
- }
- return items;
- }
-
- private static class MyListAdapter extends ArrayAdapter {
-
- private final Context mContext;
-
- public MyListAdapter(final Context context, final ArrayList items) {
- super(items);
- mContext = context;
- }
-
- @Override
- public long getItemId(final int position) {
- return getItem(position).hashCode();
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
- }
- tv.setText("This is row number " + getItem(position));
- return tv;
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/appearanceexamples/AppearanceExamplesActivity.java b/example/src/com/haarman/listviewanimations/appearanceexamples/AppearanceExamplesActivity.java
deleted file mode 100644
index 651c061c..00000000
--- a/example/src/com/haarman/listviewanimations/appearanceexamples/AppearanceExamplesActivity.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.appearanceexamples;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBar.OnNavigationListener;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.AnimationAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.AlphaInAnimationAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.ScaleInAnimationAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.SwingBottomInAnimationAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.SwingLeftInAnimationAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.SwingRightInAnimationAdapter;
-
-import java.util.ArrayList;
-
-public class AppearanceExamplesActivity extends MyListActivity implements OnNavigationListener {
-
- private BaseAdapter mAdapter;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mAdapter = new MyAdapter(this, getItems());
-
- getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getSupportActionBar().setListNavigationCallbacks(new AnimSelectionAdapter(), this);
- getSupportActionBar().setDisplayShowTitleEnabled(false);
- }
-
- private void setAlphaAdapter() {
- AnimationAdapter animAdapter = new AlphaInAnimationAdapter(mAdapter);
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- private void setLeftAdapter() {
- AnimationAdapter animAdapter = new SwingLeftInAnimationAdapter(mAdapter);
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- private void setRightAdapter() {
- AnimationAdapter animAdapter = new SwingRightInAnimationAdapter(mAdapter);
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- private void setBottomAdapter() {
- AnimationAdapter animAdapter = new SwingBottomInAnimationAdapter(mAdapter);
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- private void setBottomRightAdapter() {
- AnimationAdapter animAdapter = new SwingBottomInAnimationAdapter(new SwingRightInAnimationAdapter(mAdapter));
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- private void setScaleAdapter() {
- AnimationAdapter animAdapter = new ScaleInAnimationAdapter(mAdapter);
- animAdapter.setAbsListView(getListView());
- getListView().setAdapter(animAdapter);
- }
-
- @Override
- public boolean onNavigationItemSelected(final int itemPosition, final long itemId) {
- switch (itemPosition) {
- case 0:
- setAlphaAdapter();
- return true;
- case 1:
- setLeftAdapter();
- return true;
- case 2:
- setRightAdapter();
- return true;
- case 3:
- setBottomAdapter();
- return true;
- case 4:
- setBottomRightAdapter();
- return true;
- case 5:
- setScaleAdapter();
- return true;
- default:
- return false;
- }
- }
-
- /* Non-ListViewAnimations related stuff below */
-
- private static class MyAdapter extends ArrayAdapter {
-
- private final Context mContext;
-
- public MyAdapter(final Context context, final ArrayList items) {
- super(items);
- mContext = context;
- }
-
- @Override
- public long getItemId(final int position) {
- return getItem(position).hashCode();
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
- }
- tv.setText("This is row number " + getItem(position));
- return tv;
- }
- }
-
- private class AnimSelectionAdapter extends ArrayAdapter {
-
- public AnimSelectionAdapter() {
- addAll("Alpha", "Left", "Right", "Bottom", "Bottom right", "Scale");
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = (TextView) LayoutInflater.from(AppearanceExamplesActivity.this).inflate(android.R.layout.simple_list_item_1, parent, false);
- }
-
- tv.setText(getItem(position));
-
- return tv;
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateAdditionActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateAdditionActivity.java
deleted file mode 100644
index 5ed73c6c..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateAdditionActivity.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.AnimateAdditionAdapter;
-
-import java.util.ArrayList;
-
-public class AnimateAdditionActivity extends MyListActivity implements AdapterView.OnItemClickListener {
-
- private int mAddedItemNumber;
- private AnimateAdditionAdapter mAnimateAdditionAdapter;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- MyAdapter myAdapter = new MyAdapter(this, getStringItems());
-
- mAnimateAdditionAdapter = new AnimateAdditionAdapter(myAdapter);
- mAnimateAdditionAdapter.setListView(getListView());
-
- getListView().setAdapter(mAnimateAdditionAdapter);
- getListView().setOnItemClickListener(this);
-
- Toast.makeText(this, "Tap on an item to insert a new item", Toast.LENGTH_LONG).show();
- }
-
- private static ArrayList getStringItems() {
- ArrayList items = new ArrayList();
- for (int i = 0; i < 1000; i++) {
- items.add("This is row number " + i);
- }
- return items;
- }
-
- @Override
- public void onItemClick(final AdapterView> parent, final View view, final int position, final long id) {
- mAnimateAdditionAdapter.insert(position, "This is newly added item " + mAddedItemNumber);
- mAddedItemNumber++;
- }
-
- private static class MyAdapter extends ArrayAdapter {
-
- private final Context mContext;
-
- public MyAdapter(final Context context, final ArrayList items) {
- super(items);
- mContext = context;
- }
-
- @Override
- public long getItemId(final int position) {
- return getItem(position).hashCode();
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
- }
- tv.setText(getItem(position));
- return tv;
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateDismissActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateDismissActivity.java
deleted file mode 100644
index 9edf74ec..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/AnimateDismissActivity.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
-import android.widget.CheckedTextView;
-import android.widget.ListView;
-
-import com.haarman.listviewanimations.BaseActivity;
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.AnimateDismissAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class AnimateDismissActivity extends BaseActivity {
-
- private List mSelectedPositions;
- private MyListAdapter mAdapter;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_animateremoval);
-
- mSelectedPositions = new ArrayList();
-
- ListView listView = (ListView) findViewById(R.id.activity_animateremoval_listview);
- mAdapter = new MyListAdapter(MyListActivity.getItems());
- final AnimateDismissAdapter animateDismissAdapter = new AnimateDismissAdapter(mAdapter, new MyOnDismissCallback());
- animateDismissAdapter.setAbsListView(listView);
- listView.setAdapter(animateDismissAdapter);
-
- Button button = (Button) findViewById(R.id.activity_animateremoval_button);
- button.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(final View v) {
- animateDismissAdapter.animateDismiss(mSelectedPositions);
- mSelectedPositions.clear();
- }
- });
-
- listView.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(final AdapterView> parent, final View view, final int position, final long id) {
- CheckedTextView tv = (CheckedTextView) view;
- tv.toggle();
- if (tv.isChecked()) {
- mSelectedPositions.add(position);
- } else {
- mSelectedPositions.remove((Integer) position);
- }
- }
- });
- }
-
- private class MyOnDismissCallback implements OnDismissCallback {
-
- @Override
- public void onDismiss(final AbsListView listView, final int[] reverseSortedPositions) {
- for (int position : reverseSortedPositions) {
- mAdapter.remove(position);
- }
- }
- }
-
- private class MyListAdapter extends ArrayAdapter {
-
- public MyListAdapter(final ArrayList items) {
- super(items);
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- CheckedTextView tv = (CheckedTextView) convertView;
- if (tv == null) {
- tv = (CheckedTextView) LayoutInflater.from(AnimateDismissActivity.this).inflate(R.layout.activity_animateremoval_row, parent, false);
- }
- tv.setText(String.valueOf(getItem(position)));
- tv.setChecked(mSelectedPositions.contains(position));
- return tv;
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/DragAndDropActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/DragAndDropActivity.java
deleted file mode 100644
index 848be33e..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/DragAndDropActivity.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.os.Bundle;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.AlphaInAnimationAdapter;
-import com.nhaarman.listviewanimations.widget.DynamicListView;
-
-public class DragAndDropActivity extends MyListActivity {
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_draganddrop);
-
- DynamicListView listView = (DynamicListView) findViewById(R.id.activity_draganddrop_listview);
- listView.setDivider(null);
-
- TextView headerView =new TextView(this);
- headerView.setText("HEADER");
- listView.addHeaderView(headerView);
-
- final ArrayAdapter adapter = createListAdapter();
- AlphaInAnimationAdapter animAdapter = new AlphaInAnimationAdapter(adapter);
- animAdapter.setInitialDelayMillis(300);
- animAdapter.setAbsListView(listView);
- listView.setAdapter(animAdapter);
-
- Toast.makeText(this, "Long press an item to start dragging", Toast.LENGTH_LONG).show();
- listView.setOnItemMovedListener(new DynamicListView.OnItemMovedListener() {
- @Override
- public void onItemMoved(final int newPosition) {
- Toast.makeText(getApplicationContext(), adapter.getItem(newPosition) + " moved to position " + newPosition, Toast.LENGTH_SHORT).show();
- }
- });
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java
deleted file mode 100644
index 920a33e4..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.support.v4.util.LruCache;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.itemmanipulation.ExpandableListItemAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.prepared.AlphaInAnimationAdapter;
-
-import java.util.List;
-
-public class ExpandableListItemActivity extends MyListActivity {
-
- private MyExpandableListItemAdapter mExpandableListItemAdapter;
- private boolean mLimited;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExpandableListItemAdapter = new MyExpandableListItemAdapter(this, getItems());
- AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(mExpandableListItemAdapter);
- alphaInAnimationAdapter.setAbsListView(getListView());
- alphaInAnimationAdapter.setInitialDelayMillis(500);
- getListView().setAdapter(alphaInAnimationAdapter);
-
- Toast.makeText(this, R.string.explainexpand, Toast.LENGTH_LONG).show();
- }
-
- private static class MyExpandableListItemAdapter extends ExpandableListItemAdapter {
-
- private final Context mContext;
- private final LruCache mMemoryCache;
-
- /**
- * Creates a new ExpandableListItemAdapter with the specified list, or an empty list if
- * items == null.
- */
- private MyExpandableListItemAdapter(final Context context, final List items) {
- super(context, R.layout.activity_expandablelistitem_card, R.id.activity_expandablelistitem_card_title, R.id.activity_expandablelistitem_card_content, items);
- mContext = context;
-
- final int cacheSize = (int) (Runtime.getRuntime().maxMemory() / 1024);
- mMemoryCache = new LruCache(cacheSize) {
- @Override
- protected int sizeOf(final Integer key, final Bitmap bitmap) {
- // The cache size will be measured in kilobytes rather than
- // number of items.
- return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
- }
- };
- }
-
- @Override
- public View getTitleView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = new TextView(mContext);
- }
- tv.setText(mContext.getString(R.string.expandorcollapsecard, getItem(position)));
- return tv;
- }
-
- @Override
- public View getContentView(final int position, final View convertView, final ViewGroup parent) {
- ImageView imageView = (ImageView) convertView;
- if (imageView == null) {
- imageView = new ImageView(mContext);
- imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
- }
-
- int imageResId;
- switch (getItem(position) % 5) {
- case 0:
- imageResId = R.drawable.img_nature1;
- break;
- case 1:
- imageResId = R.drawable.img_nature2;
- break;
- case 2:
- imageResId = R.drawable.img_nature3;
- break;
- case 3:
- imageResId = R.drawable.img_nature4;
- break;
- default:
- imageResId = R.drawable.img_nature5;
- }
-
- Bitmap bitmap = getBitmapFromMemCache(imageResId);
- if (bitmap == null) {
- bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
- addBitmapToMemoryCache(imageResId, bitmap);
- }
- imageView.setImageBitmap(bitmap);
-
- return imageView;
- }
-
- private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
- if (getBitmapFromMemCache(key) == null) {
- mMemoryCache.put(key, bitmap);
- }
- }
-
- private Bitmap getBitmapFromMemCache(final int key) {
- return mMemoryCache.get(key);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(final Menu menu) {
- getMenuInflater().inflate(R.menu.menu_expandablelistitem, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_expandable_limit:
- mLimited = !mLimited;
- item.setChecked(mLimited);
- mExpandableListItemAdapter.setLimit(mLimited ? 2 : 0);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java
deleted file mode 100644
index 6a2f9cf6..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.annotation.SuppressLint;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
-import android.view.MenuItem;
-import android.view.View;
-
-import com.haarman.listviewanimations.R;
-
-public class ItemManipulationsExamplesActivity extends ActionBarActivity {
-
- @SuppressLint("InlinedApi")
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- }
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_examples_itemmanipulations);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
- public void onDragAndDropClicked(final View view) {
- Intent intent = new Intent(this, DragAndDropActivity.class);
- startActivity(intent);
- }
-
- public void onSwipeDismissClicked(final View view) {
- Intent intent = new Intent(this, SwipeDismissActivity.class);
- startActivity(intent);
- }
-
- public void onAnimateRemovalClicked(final View view) {
- Intent intent = new Intent(this, AnimateDismissActivity.class);
- startActivity(intent);
- }
-
- public void onAnimateAdditionClicked(final View view) {
- Intent intent = new Intent(this, AnimateAdditionActivity.class);
- startActivity(intent);
- }
-
- public void onExpandListItemAdapterClicked(final View view) {
- Intent intent = new Intent(this, ExpandableListItemActivity.class);
- startActivity(intent);
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/SwipeDismissActivity.java b/example/src/com/haarman/listviewanimations/itemmanipulationexamples/SwipeDismissActivity.java
deleted file mode 100644
index c8bbb236..00000000
--- a/example/src/com/haarman/listviewanimations/itemmanipulationexamples/SwipeDismissActivity.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.haarman.listviewanimations.itemmanipulationexamples;
-
-import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBar.OnNavigationListener;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.haarman.listviewanimations.MyListActivity;
-import com.haarman.listviewanimations.R;
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo.ContextualUndoAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo.ContextualUndoAdapter.CountDownFormatter;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo.ContextualUndoAdapter.DeleteItemCallback;
-
-import java.util.Arrays;
-
-public class SwipeDismissActivity extends MyListActivity implements OnNavigationListener, OnDismissCallback, DeleteItemCallback {
-
- private ArrayAdapter mAdapter;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mAdapter = createListAdapter();
-
- setSwipeDismissAdapter();
-
- getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getSupportActionBar().setListNavigationCallbacks(new AnimSelectionAdapter(), this);
- getSupportActionBar().setDisplayShowTitleEnabled(false);
- }
-
- private void setSwipeDismissAdapter() {
- SwipeDismissAdapter adapter = new SwipeDismissAdapter(mAdapter, this);
- adapter.setAbsListView(getListView());
- getListView().setAdapter(adapter);
- }
-
- @Override
- public void onDismiss(final AbsListView listView, final int[] reverseSortedPositions) {
- for (int position : reverseSortedPositions) {
- mAdapter.remove(position);
- }
- Toast.makeText(this, "Removed positions: " + Arrays.toString(reverseSortedPositions), Toast.LENGTH_SHORT).show();
- }
-
- private void setContextualUndoAdapter() {
- ContextualUndoAdapter adapter = new ContextualUndoAdapter(mAdapter, R.layout.undo_row, R.id.undo_row_undobutton, this);
- adapter.setAbsListView(getListView());
- getListView().setAdapter(adapter);
- }
-
- @Override
- public void deleteItem(final int position) {
- mAdapter.remove(position);
- mAdapter.notifyDataSetChanged();
- }
-
- private void setContextualUndoWithTimedDeleteAdapter() {
- ContextualUndoAdapter adapter = new ContextualUndoAdapter(mAdapter, R.layout.undo_row, R.id.undo_row_undobutton, 3000, this);
- adapter.setAbsListView(getListView());
- getListView().setAdapter(adapter);
- }
-
- private void setContextualUndoWithTimedDeleteAndCountDownAdapter() {
- ContextualUndoAdapter adapter = new ContextualUndoAdapter(mAdapter, R.layout.undo_row, R.id.undo_row_undobutton, 3000, R.id.undo_row_texttv, this, new MyFormatCountDownCallback());
- adapter.setAbsListView(getListView());
- getListView().setAdapter(adapter);
- }
-
- private class MyFormatCountDownCallback implements CountDownFormatter {
-
- @Override
- public String getCountDownString(final long millisUntilFinished) {
- int seconds = (int) Math.ceil(millisUntilFinished / 1000.0);
-
- if (seconds > 0) {
- return getResources().getQuantityString(R.plurals.countdown_seconds, seconds, seconds);
- }
- return getString(R.string.countdown_dismissing);
- }
- }
-
- /* Non-ListViewAnimations related stuff below */
-
- @Override
- public boolean onNavigationItemSelected(final int itemPosition, final long itemId) {
- switch (itemPosition) {
- case 0:
- setSwipeDismissAdapter();
- return true;
- case 1:
- setContextualUndoAdapter();
- return true;
- case 2:
- setContextualUndoWithTimedDeleteAdapter();
- return true;
- case 3:
- setContextualUndoWithTimedDeleteAndCountDownAdapter();
- return true;
- default:
- return false;
- }
- }
-
- private class AnimSelectionAdapter extends ArrayAdapter {
-
- public AnimSelectionAdapter() {
- addAll("Swipe-To-Dismiss", "Contextual Undo", "CU - Timed Delete", "CU - Count Down");
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- TextView tv = (TextView) convertView;
- if (tv == null) {
- tv = (TextView) LayoutInflater.from(SwipeDismissActivity.this).inflate(android.R.layout.simple_list_item_1, parent, false);
- }
-
- tv.setText(getItem(position));
-
- return tv;
- }
- }
-}
diff --git a/example/src/main/AndroidManifest.xml b/example/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..3c16d86c
--- /dev/null
+++ b/example/src/main/AndroidManifest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/com/android/vending/billing/IInAppBillingService.aidl b/example/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
similarity index 100%
rename from example/src/com/android/vending/billing/IInAppBillingService.aidl
rename to example/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl
diff --git a/example/src/main/java/com/haarman/listviewanimations/BaseActivity.java b/example/src/main/java/com/haarman/listviewanimations/BaseActivity.java
new file mode 100644
index 00000000..2908a826
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/BaseActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.WindowManager;
+
+public class BaseActivity extends Activity {
+
+ @SuppressLint("InlinedApi")
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ }
+ super.onCreate(savedInstanceState);
+
+ assert getActionBar() != null;
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/MainActivity.java b/example/src/main/java/com/haarman/listviewanimations/MainActivity.java
new file mode 100644
index 00000000..297c6e0c
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/MainActivity.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import com.android.vending.billing.IInAppBillingService;
+import com.haarman.listviewanimations.appearance.AppearanceExamplesActivity;
+import com.haarman.listviewanimations.googlecards.GoogleCardsActivity;
+import com.haarman.listviewanimations.gridview.GridViewActivity;
+import com.haarman.listviewanimations.itemmanipulation.ItemManipulationsExamplesActivity;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class MainActivity extends Activity {
+
+ private static final String URL_GITHUB_IO = "http://nhaarman.github.io/ListViewAnimations?ref=app";
+
+ private final ServiceConnection mServiceConn = new MyServiceConnection();
+ private IInAppBillingService mService;
+
+ @SuppressLint("InlinedApi")
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ }
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"), mServiceConn, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+
+ menu.findItem(R.id.menu_main_donate).setVisible(mService != null);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_main_github:
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(URL_GITHUB_IO));
+ startActivity(intent);
+ return true;
+ case R.id.menu_main_beer:
+ buy("beer");
+ return true;
+ case R.id.menu_main_beer2:
+ buy("beer2");
+ return true;
+ case R.id.menu_main_beer3:
+ buy("beer3");
+ return true;
+ case R.id.menu_main_beer4:
+ buy("beer4");
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ public void onGoogleCardsExampleClicked(final View view) {
+ Intent intent = new Intent(this, GoogleCardsActivity.class);
+ startActivity(intent);
+ }
+
+ public void onGridViewExampleClicked(final View view) {
+ Intent intent = new Intent(this, GridViewActivity.class);
+ startActivity(intent);
+ }
+
+ public void onAppearanceClicked(final View view) {
+ Intent intent = new Intent(this, AppearanceExamplesActivity.class);
+ startActivity(intent);
+ }
+
+ public void onItemManipulationClicked(final View view) {
+ Intent intent = new Intent(this, ItemManipulationsExamplesActivity.class);
+ startActivity(intent);
+ }
+
+ public void onSLHClicked(final View view) {
+ Intent intent = new Intent(this, StickyListHeadersActivity.class);
+ startActivity(intent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unbindService(mServiceConn);
+ }
+
+ @Override
+ protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode == RESULT_OK) {
+ Toast.makeText(this, getString(R.string.thanks), Toast.LENGTH_LONG).show();
+
+ new Thread(new ConsumePurchaseRunnable(data)).start();
+ }
+ }
+
+ private void buy(final String sku) {
+ try {
+ Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", "bGoa+V7g/ysDXvKwqq+JTFn4uQZbPiQJo4pf9RzJ");
+ PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
+ if (pendingIntent != null) {
+ startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), 0, 0, 0);
+ }
+ } catch (RemoteException | SendIntentException ignored) {
+ Toast.makeText(this, getString(R.string.exception), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private class MyServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceDisconnected(final ComponentName name) {
+ mService = null;
+ }
+
+ @Override
+ public void onServiceConnected(final ComponentName name, final IBinder service) {
+ mService = IInAppBillingService.Stub.asInterface(service);
+
+ new Thread(new RetrievePurchasesRunnable()).start();
+ }
+ }
+
+ private class RetrievePurchasesRunnable implements Runnable {
+ @Override
+ public void run() {
+ try {
+ Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
+
+ int response = ownedItems.getInt("RESPONSE_CODE");
+ if (response == 0) {
+ Iterable purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST");
+
+ if (purchaseDataList != null) {
+ for (String purchaseData : purchaseDataList) {
+ JSONObject json = new JSONObject(purchaseData);
+ mService.consumePurchase(3, getPackageName(), json.getString("purchaseToken"));
+ }
+ }
+ }
+ } catch (RemoteException | JSONException ignored) {
+ Toast.makeText(MainActivity.this, getString(R.string.exception), Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ private class ConsumePurchaseRunnable implements Runnable {
+ private final Intent mData;
+
+ ConsumePurchaseRunnable(final Intent data) {
+ mData = data;
+ }
+
+ @Override
+ public void run() {
+ try {
+ JSONObject json = new JSONObject(mData.getStringExtra("INAPP_PURCHASE_DATA"));
+ mService.consumePurchase(3, getPackageName(), json.getString("purchaseToken"));
+ } catch (JSONException | RemoteException ignored) {
+ Toast.makeText(MainActivity.this, getString(R.string.exception), Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/MyListActivity.java b/example/src/main/java/com/haarman/listviewanimations/MyListActivity.java
new file mode 100644
index 00000000..939fae65
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/MyListActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations;
+
+import android.os.Bundle;
+import android.widget.ListView;
+
+public class MyListActivity extends BaseActivity {
+
+ private ListView mListView;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mylist);
+ mListView = (ListView) findViewById(R.id.activity_mylist_listview);
+ }
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ protected MyListAdapter createListAdapter() {
+ return new MyListAdapter(this);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/MyListAdapter.java b/example/src/main/java/com/haarman/listviewanimations/MyListAdapter.java
new file mode 100644
index 00000000..50117907
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/MyListAdapter.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.nhaarman.listviewanimations.ArrayAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter;
+
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+
+public class MyListAdapter extends ArrayAdapter implements UndoAdapter, StickyListHeadersAdapter {
+
+ private final Context mContext;
+
+ public MyListAdapter(final Context context) {
+ mContext = context;
+ for (int i = 0; i < 1000; i++) {
+ add(mContext.getString(R.string.row_number, i));
+ }
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return getItem(position).hashCode();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
+ }
+
+ view.setText(getItem(position));
+
+ return view;
+ }
+
+ @NonNull
+ @Override
+ public View getUndoView(final int position, final View convertView, @NonNull final ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ view = LayoutInflater.from(mContext).inflate(R.layout.undo_row, parent, false);
+ }
+ return view;
+ }
+
+ @NonNull
+ @Override
+ public View getUndoClickView(@NonNull final View view) {
+ return view.findViewById(R.id.undo_row_undobutton);
+ }
+
+ @Override
+ public View getHeaderView(final int position, final View convertView, final ViewGroup parent) {
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_header, parent, false);
+ }
+
+ view.setText(mContext.getString(R.string.header, getHeaderId(position)));
+
+ return view;
+ }
+
+ @Override
+ public long getHeaderId(final int position) {
+ return position / 10;
+ }
+}
\ No newline at end of file
diff --git a/example/src/main/java/com/haarman/listviewanimations/StickyListHeadersActivity.java b/example/src/main/java/com/haarman/listviewanimations/StickyListHeadersActivity.java
new file mode 100644
index 00000000..65e266b3
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/StickyListHeadersActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations;
+
+import android.os.Bundle;
+
+import com.nhaarman.listviewanimations.appearance.StickyListHeadersAdapterDecorator;
+import com.nhaarman.listviewanimations.appearance.simple.AlphaInAnimationAdapter;
+import com.nhaarman.listviewanimations.util.StickyListHeadersListViewWrapper;
+
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+
+public class StickyListHeadersActivity extends BaseActivity {
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_stickylistheaders);
+
+ StickyListHeadersListView listView = (StickyListHeadersListView) findViewById(R.id.activity_stickylistheaders_listview);
+ listView.setFitsSystemWindows(true);
+
+ MyListAdapter adapter = new MyListAdapter(this);
+ AlphaInAnimationAdapter animationAdapter = new AlphaInAnimationAdapter(adapter);
+ StickyListHeadersAdapterDecorator stickyListHeadersAdapterDecorator = new StickyListHeadersAdapterDecorator(animationAdapter);
+ stickyListHeadersAdapterDecorator.setListViewWrapper(new StickyListHeadersListViewWrapper(listView));
+
+ assert animationAdapter.getViewAnimator() != null;
+ animationAdapter.getViewAnimator().setInitialDelayMillis(500);
+
+ assert stickyListHeadersAdapterDecorator.getViewAnimator() != null;
+ stickyListHeadersAdapterDecorator.getViewAnimator().setInitialDelayMillis(500);
+
+ listView.setAdapter(stickyListHeadersAdapterDecorator);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/appearance/AppearanceExamplesActivity.java b/example/src/main/java/com/haarman/listviewanimations/appearance/AppearanceExamplesActivity.java
new file mode 100644
index 00000000..3130eab4
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/appearance/AppearanceExamplesActivity.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.appearance;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import com.haarman.listviewanimations.MyListActivity;
+import com.haarman.listviewanimations.MyListAdapter;
+import com.haarman.listviewanimations.R;
+import com.nhaarman.listviewanimations.ArrayAdapter;
+import com.nhaarman.listviewanimations.appearance.AnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.AlphaInAnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.ScaleInAnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.SwingLeftInAnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.SwingRightInAnimationAdapter;
+
+import java.util.Arrays;
+
+public class AppearanceExamplesActivity extends MyListActivity implements ActionBar.OnNavigationListener {
+
+ private static final String SAVEDINSTANCESTATE_ANIMATIONADAPTER = "savedinstancestate_animationadapter";
+
+ private BaseAdapter mAdapter;
+
+ private AnimationAdapter mAnimAdapter;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAdapter = new MyListAdapter(this);
+ setAlphaAdapter();
+
+ assert getActionBar() != null;
+ getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ getActionBar().setDisplayShowTitleEnabled(false);
+
+ SpinnerAdapter animSelectionAdapter = new AnimSelectionAdapter(this);
+ getActionBar().setListNavigationCallbacks(animSelectionAdapter, this);
+ }
+
+ private void setAlphaAdapter() {
+ if (!(mAnimAdapter instanceof AlphaInAnimationAdapter)) {
+ mAnimAdapter = new AlphaInAnimationAdapter(mAdapter);
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+ }
+
+ private void setLeftAdapter() {
+ if (!(mAnimAdapter instanceof SwingLeftInAnimationAdapter)) {
+ mAnimAdapter = new SwingLeftInAnimationAdapter(mAdapter);
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+ }
+
+ private void setRightAdapter() {
+ if (!(mAnimAdapter instanceof SwingRightInAnimationAdapter)) {
+ mAnimAdapter = new SwingRightInAnimationAdapter(mAdapter);
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+ }
+
+ private void setBottomAdapter() {
+ if (!(mAnimAdapter instanceof SwingBottomInAnimationAdapter)) {
+ mAnimAdapter = new SwingBottomInAnimationAdapter(mAdapter);
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+ }
+
+ private void setBottomRightAdapter() {
+ mAnimAdapter = new SwingBottomInAnimationAdapter(new SwingRightInAnimationAdapter(mAdapter));
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+
+ private void setScaleAdapter() {
+ if (!(mAnimAdapter instanceof ScaleInAnimationAdapter)) {
+ mAnimAdapter = new ScaleInAnimationAdapter(mAdapter);
+ mAnimAdapter.setAbsListView(getListView());
+ getListView().setAdapter(mAnimAdapter);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull final Bundle outState) {
+ outState.putParcelable(SAVEDINSTANCESTATE_ANIMATIONADAPTER, mAnimAdapter.onSaveInstanceState());
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mAnimAdapter.onRestoreInstanceState(savedInstanceState.getParcelable(SAVEDINSTANCESTATE_ANIMATIONADAPTER));
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(final int itemPosition, final long itemId) {
+ switch (itemPosition) {
+ case 0:
+ setAlphaAdapter();
+ return true;
+ case 1:
+ setLeftAdapter();
+ return true;
+ case 2:
+ setRightAdapter();
+ return true;
+ case 3:
+ setBottomAdapter();
+ return true;
+ case 4:
+ setBottomRightAdapter();
+ return true;
+ case 5:
+ setScaleAdapter();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static class AnimSelectionAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+
+ AnimSelectionAdapter(@NonNull final Context context) {
+ mContext = context;
+ String[] items = context.getResources().getStringArray(R.array.appearance_examples);
+ addAll(Arrays.asList(items));
+ }
+
+ @Override
+ public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ TextView tv = (TextView) convertView;
+ if (tv == null) {
+ tv = (TextView) LayoutInflater.from(mContext).inflate(android.R.layout.simple_list_item_1, parent, false);
+ tv.setTextColor(mContext.getResources().getColor(android.R.color.white));
+ }
+
+ tv.setText(getItem(position));
+
+ return tv;
+ }
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsActivity.java b/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsActivity.java
new file mode 100644
index 00000000..67746ee5
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.googlecards;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import com.haarman.listviewanimations.BaseActivity;
+import com.haarman.listviewanimations.R;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter;
+
+public class GoogleCardsActivity extends BaseActivity implements OnDismissCallback {
+
+ private static final int INITIAL_DELAY_MILLIS = 300;
+
+ private GoogleCardsAdapter mGoogleCardsAdapter;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_googlecards);
+
+ ListView listView = (ListView) findViewById(R.id.activity_googlecards_listview);
+
+ mGoogleCardsAdapter = new GoogleCardsAdapter(this);
+ SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(new SwipeDismissAdapter(mGoogleCardsAdapter, this));
+ swingBottomInAnimationAdapter.setAbsListView(listView);
+
+ assert swingBottomInAnimationAdapter.getViewAnimator() != null;
+ swingBottomInAnimationAdapter.getViewAnimator().setInitialDelayMillis(INITIAL_DELAY_MILLIS);
+
+ listView.setAdapter(swingBottomInAnimationAdapter);
+
+ for (int i = 0; i < 100; i++) {
+ mGoogleCardsAdapter.add(i);
+ }
+ }
+
+ @Override
+ public void onDismiss(@NonNull final ViewGroup listView, @NonNull final int[] reverseSortedPositions) {
+ for (int position : reverseSortedPositions) {
+ mGoogleCardsAdapter.remove(position);
+ }
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsAdapter.java b/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsAdapter.java
new file mode 100644
index 00000000..8d11591a
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/googlecards/GoogleCardsAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.googlecards;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.haarman.listviewanimations.R;
+import com.haarman.listviewanimations.util.BitmapCache;
+import com.nhaarman.listviewanimations.ArrayAdapter;
+
+public class GoogleCardsAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+ private final BitmapCache mMemoryCache;
+
+ GoogleCardsAdapter(final Context context) {
+ mContext = context;
+ mMemoryCache = new BitmapCache();
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ ViewHolder viewHolder;
+ View view = convertView;
+ if (view == null) {
+ view = LayoutInflater.from(mContext).inflate(R.layout.activity_googlecards_card, parent, false);
+
+ viewHolder = new ViewHolder();
+ viewHolder.textView = (TextView) view.findViewById(R.id.activity_googlecards_card_textview);
+ view.setTag(viewHolder);
+
+ viewHolder.imageView = (ImageView) view.findViewById(R.id.activity_googlecards_card_imageview);
+ } else {
+ viewHolder = (ViewHolder) view.getTag();
+ }
+
+ viewHolder.textView.setText(mContext.getString(R.string.card_number, getItem(position) + 1));
+ setImageView(viewHolder, position);
+
+ return view;
+ }
+
+ private void setImageView(final ViewHolder viewHolder, final int position) {
+ int imageResId;
+ switch (getItem(position) % 5) {
+ case 0:
+ imageResId = R.drawable.img_nature1;
+ break;
+ case 1:
+ imageResId = R.drawable.img_nature2;
+ break;
+ case 2:
+ imageResId = R.drawable.img_nature3;
+ break;
+ case 3:
+ imageResId = R.drawable.img_nature4;
+ break;
+ default:
+ imageResId = R.drawable.img_nature5;
+ }
+
+ Bitmap bitmap = getBitmapFromMemCache(imageResId);
+ if (bitmap == null) {
+ bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
+ addBitmapToMemoryCache(imageResId, bitmap);
+ }
+ viewHolder.imageView.setImageBitmap(bitmap);
+ }
+
+ private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+ }
+
+ private Bitmap getBitmapFromMemCache(final int key) {
+ return mMemoryCache.get(key);
+ }
+
+ @SuppressWarnings({"PackageVisibleField", "InstanceVariableNamingConvention"})
+ private static class ViewHolder {
+ TextView textView;
+ ImageView imageView;
+ }
+}
\ No newline at end of file
diff --git a/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewActivity.java b/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewActivity.java
new file mode 100644
index 00000000..c9c7fa8c
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewActivity.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.gridview;
+
+import android.os.Bundle;
+import android.widget.GridView;
+
+import com.haarman.listviewanimations.BaseActivity;
+import com.haarman.listviewanimations.R;
+import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter;
+
+public class GridViewActivity extends BaseActivity {
+
+ private static final int INITIAL_DELAY_MILLIS = 300;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_gridview);
+
+ GridView gridView = (GridView) findViewById(R.id.activity_gridview_gv);
+ SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(new GridViewAdapter(this));
+ swingBottomInAnimationAdapter.setAbsListView(gridView);
+
+ assert swingBottomInAnimationAdapter.getViewAnimator() != null;
+ swingBottomInAnimationAdapter.getViewAnimator().setInitialDelayMillis(INITIAL_DELAY_MILLIS);
+
+ gridView.setAdapter(swingBottomInAnimationAdapter);
+
+ assert getActionBar() != null;
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewAdapter.java b/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewAdapter.java
new file mode 100644
index 00000000..f805f378
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/gridview/GridViewAdapter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.gridview;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.haarman.listviewanimations.R;
+import com.haarman.listviewanimations.util.BitmapCache;
+import com.nhaarman.listviewanimations.ArrayAdapter;
+
+public class GridViewAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+ private final BitmapCache mMemoryCache;
+
+ public GridViewAdapter(final Context context) {
+ mContext = context;
+ mMemoryCache = new BitmapCache();
+
+ for (int i = 0; i < 1000; i++) {
+ add(i);
+ }
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ ImageView imageView = (ImageView) convertView;
+
+ if (imageView == null) {
+ imageView = new ImageView(mContext);
+ imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ }
+
+ int imageResId;
+ switch (getItem(position) % 5) {
+ case 0:
+ imageResId = R.drawable.img_nature1;
+ break;
+ case 1:
+ imageResId = R.drawable.img_nature2;
+ break;
+ case 2:
+ imageResId = R.drawable.img_nature3;
+ break;
+ case 3:
+ imageResId = R.drawable.img_nature4;
+ break;
+ default:
+ imageResId = R.drawable.img_nature5;
+ }
+
+ Bitmap bitmap = getBitmapFromMemCache(imageResId);
+ if (bitmap == null) {
+ bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
+ addBitmapToMemoryCache(imageResId, bitmap);
+ }
+ imageView.setImageBitmap(bitmap);
+
+ return imageView;
+ }
+
+ private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+ }
+
+ private Bitmap getBitmapFromMemCache(final int key) {
+ return mMemoryCache.get(key);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/DynamicListViewActivity.java b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/DynamicListViewActivity.java
new file mode 100644
index 00000000..c83da434
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/DynamicListViewActivity.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.itemmanipulation;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.haarman.listviewanimations.MyListActivity;
+import com.haarman.listviewanimations.R;
+import com.nhaarman.listviewanimations.ArrayAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.OnItemMovedListener;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.TouchViewDraggableManager;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.SimpleSwipeUndoAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter;
+import com.nhaarman.listviewanimations.appearance.simple.AlphaInAnimationAdapter;
+
+public class DynamicListViewActivity extends MyListActivity {
+
+ private static final int INITIAL_DELAY_MILLIS = 300;
+
+ private int mNewItemCount;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_dynamiclistview);
+
+ DynamicListView listView = (DynamicListView) findViewById(R.id.activity_dynamiclistview_listview);
+ listView.addHeaderView(LayoutInflater.from(this).inflate(R.layout.activity_dynamiclistview_header, listView, false));
+
+ /* Setup the adapter */
+ ArrayAdapter adapter = new MyListAdapter(this);
+ SimpleSwipeUndoAdapter simpleSwipeUndoAdapter = new SimpleSwipeUndoAdapter(adapter, this, new MyOnDismissCallback(adapter));
+ AlphaInAnimationAdapter animAdapter = new AlphaInAnimationAdapter(simpleSwipeUndoAdapter);
+ animAdapter.setAbsListView(listView);
+ assert animAdapter.getViewAnimator() != null;
+ animAdapter.getViewAnimator().setInitialDelayMillis(INITIAL_DELAY_MILLIS);
+ listView.setAdapter(animAdapter);
+
+ /* Enable drag and drop functionality */
+ listView.enableDragAndDrop();
+ listView.setDraggableManager(new TouchViewDraggableManager(R.id.list_row_draganddrop_touchview));
+ listView.setOnItemMovedListener(new MyOnItemMovedListener(adapter));
+ listView.setOnItemLongClickListener(new MyOnItemLongClickListener(listView));
+
+ /* Enable swipe to dismiss */
+ listView.enableSimpleSwipeUndo();
+
+ /* Add new items on item click */
+ listView.setOnItemClickListener(new MyOnItemClickListener(listView));
+ }
+
+ private static class MyListAdapter extends ArrayAdapter implements UndoAdapter {
+
+ private final Context mContext;
+
+ MyListAdapter(final Context context) {
+ mContext = context;
+ for (int i = 0; i < 1000; i++) {
+ add(mContext.getString(R.string.row_number, i));
+ }
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return getItem(position).hashCode();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ view = LayoutInflater.from(mContext).inflate(R.layout.list_row_dynamiclistview, parent, false);
+ }
+
+ ((TextView) view.findViewById(R.id.list_row_draganddrop_textview)).setText(getItem(position));
+
+ return view;
+ }
+
+ @NonNull
+ @Override
+ public View getUndoView(final int position, final View convertView, @NonNull final ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ view = LayoutInflater.from(mContext).inflate(R.layout.undo_row, parent, false);
+ }
+ return view;
+ }
+
+ @NonNull
+ @Override
+ public View getUndoClickView(@NonNull final View view) {
+ return view.findViewById(R.id.undo_row_undobutton);
+ }
+ }
+
+ private static class MyOnItemLongClickListener implements AdapterView.OnItemLongClickListener {
+
+ private final DynamicListView mListView;
+
+ MyOnItemLongClickListener(final DynamicListView listView) {
+ mListView = listView;
+ }
+
+ @Override
+ public boolean onItemLongClick(final AdapterView> parent, final View view, final int position, final long id) {
+ if (mListView != null) {
+ mListView.startDragging(position - mListView.getHeaderViewsCount());
+ }
+ return true;
+ }
+ }
+
+ private static class MyOnDismissCallback implements OnDismissCallback {
+
+ private final ArrayAdapter mAdapter;
+
+ MyOnDismissCallback(final ArrayAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public void onDismiss(@NonNull final ViewGroup listView, @NonNull final int[] reverseSortedPositions) {
+ for (int position : reverseSortedPositions) {
+ mAdapter.remove(position);
+ }
+ }
+ }
+
+ private class MyOnItemMovedListener implements OnItemMovedListener {
+
+ private final ArrayAdapter mAdapter;
+
+ private Toast mToast;
+
+ MyOnItemMovedListener(final ArrayAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public void onItemMoved(final int originalPosition, final int newPosition) {
+ if (mToast != null) {
+ mToast.cancel();
+ }
+
+ mToast = Toast.makeText(getApplicationContext(), getString(R.string.moved, mAdapter.getItem(newPosition), newPosition), Toast.LENGTH_SHORT);
+ mToast.show();
+ }
+ }
+
+ private class MyOnItemClickListener implements AdapterView.OnItemClickListener {
+
+ private final DynamicListView mListView;
+
+ MyOnItemClickListener(final DynamicListView listView) {
+ mListView = listView;
+ }
+
+ @Override
+ public void onItemClick(final AdapterView> parent, final View view, final int position, final long id) {
+ mListView.insert(position, getString(R.string.newly_added_item, mNewItemCount));
+ mNewItemCount++;
+ }
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/ItemManipulationsExamplesActivity.java b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/ItemManipulationsExamplesActivity.java
new file mode 100644
index 00000000..49fc5ff3
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/ItemManipulationsExamplesActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.itemmanipulation;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import com.haarman.listviewanimations.BaseActivity;
+import com.haarman.listviewanimations.R;
+import com.haarman.listviewanimations.itemmanipulation.expandablelistitems.ExpandableListItemActivity;
+
+public class ItemManipulationsExamplesActivity extends BaseActivity {
+
+ @SuppressLint("InlinedApi")
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_examples_itemmanipulations);
+ }
+
+ public void onDynamicListViewClicked(final View view) {
+ Intent intent = new Intent(this, DynamicListViewActivity.class);
+ startActivity(intent);
+ }
+
+ public void onExpandListItemAdapterClicked(final View view) {
+ Intent intent = new Intent(this, ExpandableListItemActivity.class);
+ startActivity(intent);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/ExpandableListItemActivity.java b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/ExpandableListItemActivity.java
new file mode 100644
index 00000000..48111c8f
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/ExpandableListItemActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.itemmanipulation.expandablelistitems;
+
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.haarman.listviewanimations.MyListActivity;
+import com.haarman.listviewanimations.R;
+import com.nhaarman.listviewanimations.appearance.simple.AlphaInAnimationAdapter;
+
+public class ExpandableListItemActivity extends MyListActivity {
+
+ private static final int INITIAL_DELAY_MILLIS = 500;
+ private MyExpandableListItemAdapter mExpandableListItemAdapter;
+
+ private boolean mLimited;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mExpandableListItemAdapter = new MyExpandableListItemAdapter(this);
+ AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(mExpandableListItemAdapter);
+ alphaInAnimationAdapter.setAbsListView(getListView());
+
+ assert alphaInAnimationAdapter.getViewAnimator() != null;
+ alphaInAnimationAdapter.getViewAnimator().setInitialDelayMillis(INITIAL_DELAY_MILLIS);
+
+ getListView().setAdapter(alphaInAnimationAdapter);
+
+ Toast.makeText(this, R.string.explainexpand, Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_expandablelistitem, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_expandable_limit:
+ mLimited = !mLimited;
+ item.setChecked(mLimited);
+ mExpandableListItemAdapter.setLimit(mLimited ? 2 : 0);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/MyExpandableListItemAdapter.java b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/MyExpandableListItemAdapter.java
new file mode 100644
index 00000000..6a5ea954
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/itemmanipulation/expandablelistitems/MyExpandableListItemAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.itemmanipulation.expandablelistitems;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.haarman.listviewanimations.R;
+import com.haarman.listviewanimations.util.BitmapCache;
+import com.nhaarman.listviewanimations.itemmanipulation.expandablelistitem.ExpandableListItemAdapter;
+
+public class MyExpandableListItemAdapter extends ExpandableListItemAdapter {
+
+ private final Context mContext;
+ private final BitmapCache mMemoryCache;
+
+ /**
+ * Creates a new ExpandableListItemAdapter with the specified list, or an empty list if
+ * items == null.
+ */
+ public MyExpandableListItemAdapter(final Context context) {
+ super(context, R.layout.activity_expandablelistitem_card, R.id.activity_expandablelistitem_card_title, R.id.activity_expandablelistitem_card_content);
+ mContext = context;
+ mMemoryCache = new BitmapCache();
+
+ for (int i = 0; i < 100; i++) {
+ add(i);
+ }
+ }
+
+ @NonNull
+ @Override
+ public View getTitleView(final int position, final View convertView, @NonNull final ViewGroup parent) {
+ TextView tv = (TextView) convertView;
+ if (tv == null) {
+ tv = new TextView(mContext);
+ }
+ tv.setText(mContext.getString(R.string.expandorcollapsecard, (int) getItem(position)));
+ return tv;
+ }
+
+ @NonNull
+ @Override
+ public View getContentView(final int position, final View convertView, @NonNull final ViewGroup parent) {
+ ImageView imageView = (ImageView) convertView;
+ if (imageView == null) {
+ imageView = new ImageView(mContext);
+ imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ }
+
+ int imageResId;
+ switch (getItem(position) % 5) {
+ case 0:
+ imageResId = R.drawable.img_nature1;
+ break;
+ case 1:
+ imageResId = R.drawable.img_nature2;
+ break;
+ case 2:
+ imageResId = R.drawable.img_nature3;
+ break;
+ case 3:
+ imageResId = R.drawable.img_nature4;
+ break;
+ default:
+ imageResId = R.drawable.img_nature5;
+ }
+
+ Bitmap bitmap = getBitmapFromMemCache(imageResId);
+ if (bitmap == null) {
+ bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
+ addBitmapToMemoryCache(imageResId, bitmap);
+ }
+ imageView.setImageBitmap(bitmap);
+
+ return imageView;
+ }
+
+ private void addBitmapToMemoryCache(final int key, final Bitmap bitmap) {
+ if (getBitmapFromMemCache(key) == null) {
+ mMemoryCache.put(key, bitmap);
+ }
+ }
+
+ private Bitmap getBitmapFromMemCache(final int key) {
+ return mMemoryCache.get(key);
+ }
+}
\ No newline at end of file
diff --git a/example/src/main/java/com/haarman/listviewanimations/util/BitmapCache.java b/example/src/main/java/com/haarman/listviewanimations/util/BitmapCache.java
new file mode 100644
index 00000000..fc47282a
--- /dev/null
+++ b/example/src/main/java/com/haarman/listviewanimations/util/BitmapCache.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.haarman.listviewanimations.util;
+
+import android.graphics.Bitmap;
+import android.util.LruCache;
+
+public class BitmapCache extends LruCache {
+
+ private static final int KILO = 1024;
+ private static final int MEMORY_FACTOR = 2 * KILO;
+
+ public BitmapCache() {
+ super((int) (Runtime.getRuntime().maxMemory() / MEMORY_FACTOR));
+ }
+
+ @Override
+ protected int sizeOf(final Integer key, final Bitmap value) {
+ return value.getRowBytes() * value.getHeight() / KILO;
+ }
+}
diff --git a/example/res/drawable-hdpi/ic_beer.png b/example/src/main/res/drawable-hdpi/ic_beer.png
similarity index 59%
rename from example/res/drawable-hdpi/ic_beer.png
rename to example/src/main/res/drawable-hdpi/ic_beer.png
index 30a3ecab..133c909a 100644
Binary files a/example/res/drawable-hdpi/ic_beer.png and b/example/src/main/res/drawable-hdpi/ic_beer.png differ
diff --git a/example/res/drawable-hdpi/ic_github.png b/example/src/main/res/drawable-hdpi/ic_github.png
similarity index 61%
rename from example/res/drawable-hdpi/ic_github.png
rename to example/src/main/res/drawable-hdpi/ic_github.png
index d76e5725..3b0ec56a 100644
Binary files a/example/res/drawable-hdpi/ic_github.png and b/example/src/main/res/drawable-hdpi/ic_github.png differ
diff --git a/example/res/drawable-hdpi/ic_launcher.png b/example/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from example/res/drawable-hdpi/ic_launcher.png
rename to example/src/main/res/drawable-hdpi/ic_launcher.png
diff --git a/example/res/drawable-hdpi/ic_undo.png b/example/src/main/res/drawable-hdpi/ic_undo.png
similarity index 100%
rename from example/res/drawable-hdpi/ic_undo.png
rename to example/src/main/res/drawable-hdpi/ic_undo.png
diff --git a/example/res/drawable-mdpi/ic_beer.png b/example/src/main/res/drawable-mdpi/ic_beer.png
similarity index 68%
rename from example/res/drawable-mdpi/ic_beer.png
rename to example/src/main/res/drawable-mdpi/ic_beer.png
index 82c5c032..1519b7c2 100644
Binary files a/example/res/drawable-mdpi/ic_beer.png and b/example/src/main/res/drawable-mdpi/ic_beer.png differ
diff --git a/example/res/drawable-mdpi/ic_github.png b/example/src/main/res/drawable-mdpi/ic_github.png
similarity index 71%
rename from example/res/drawable-mdpi/ic_github.png
rename to example/src/main/res/drawable-mdpi/ic_github.png
index eb56a6dc..55d82da4 100644
Binary files a/example/res/drawable-mdpi/ic_github.png and b/example/src/main/res/drawable-mdpi/ic_github.png differ
diff --git a/example/res/drawable-mdpi/ic_launcher.png b/example/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from example/res/drawable-mdpi/ic_launcher.png
rename to example/src/main/res/drawable-mdpi/ic_launcher.png
diff --git a/example/res/drawable-mdpi/ic_undo.png b/example/src/main/res/drawable-mdpi/ic_undo.png
similarity index 100%
rename from example/res/drawable-mdpi/ic_undo.png
rename to example/src/main/res/drawable-mdpi/ic_undo.png
diff --git a/example/src/main/res/drawable-xhdpi/ic_beer.png b/example/src/main/res/drawable-xhdpi/ic_beer.png
new file mode 100644
index 00000000..2c747c64
Binary files /dev/null and b/example/src/main/res/drawable-xhdpi/ic_beer.png differ
diff --git a/example/src/main/res/drawable-xhdpi/ic_github.png b/example/src/main/res/drawable-xhdpi/ic_github.png
new file mode 100644
index 00000000..35338268
Binary files /dev/null and b/example/src/main/res/drawable-xhdpi/ic_github.png differ
diff --git a/example/res/drawable-xhdpi/ic_launcher.png b/example/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from example/res/drawable-xhdpi/ic_launcher.png
rename to example/src/main/res/drawable-xhdpi/ic_launcher.png
diff --git a/example/res/drawable-xhdpi/ic_undo.png b/example/src/main/res/drawable-xhdpi/ic_undo.png
similarity index 100%
rename from example/res/drawable-xhdpi/ic_undo.png
rename to example/src/main/res/drawable-xhdpi/ic_undo.png
diff --git a/example/src/main/res/drawable-xxhdpi/ab_dropdown.9.png b/example/src/main/res/drawable-xxhdpi/ab_dropdown.9.png
new file mode 100644
index 00000000..11ddfea1
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/ab_dropdown.9.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/ic_beer.png b/example/src/main/res/drawable-xxhdpi/ic_beer.png
new file mode 100644
index 00000000..c2b5ac18
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/ic_beer.png differ
diff --git a/example/res/drawable-xxhdpi/ic_github.png b/example/src/main/res/drawable-xxhdpi/ic_github.png
similarity index 56%
rename from example/res/drawable-xxhdpi/ic_github.png
rename to example/src/main/res/drawable-xxhdpi/ic_github.png
index 27086f1c..ffc33ecc 100644
Binary files a/example/res/drawable-xxhdpi/ic_github.png and b/example/src/main/res/drawable-xxhdpi/ic_github.png differ
diff --git a/example/res/drawable-xxhdpi/ic_launcher.png b/example/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from example/res/drawable-xxhdpi/ic_launcher.png
rename to example/src/main/res/drawable-xxhdpi/ic_launcher.png
diff --git a/example/res/drawable-xxhdpi/ic_undo.png b/example/src/main/res/drawable-xxhdpi/ic_undo.png
similarity index 100%
rename from example/res/drawable-xxhdpi/ic_undo.png
rename to example/src/main/res/drawable-xxhdpi/ic_undo.png
diff --git a/example/src/main/res/drawable-xxhdpi/img_main_appearance.png b/example/src/main/res/drawable-xxhdpi/img_main_appearance.png
new file mode 100644
index 00000000..e8f0e950
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_main_appearance.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_main_googlecards.png b/example/src/main/res/drawable-xxhdpi/img_main_googlecards.png
new file mode 100644
index 00000000..02ac820d
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_main_googlecards.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_main_gridview.png b/example/src/main/res/drawable-xxhdpi/img_main_gridview.png
new file mode 100644
index 00000000..7854e25f
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_main_gridview.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_main_itemmanipulation.png b/example/src/main/res/drawable-xxhdpi/img_main_itemmanipulation.png
new file mode 100644
index 00000000..e4e64757
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_main_itemmanipulation.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_main_stickylistheaders.png b/example/src/main/res/drawable-xxhdpi/img_main_stickylistheaders.png
new file mode 100644
index 00000000..c0ddd200
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_main_stickylistheaders.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_manipulation_animateaddition.png b/example/src/main/res/drawable-xxhdpi/img_manipulation_animateaddition.png
new file mode 100644
index 00000000..efcb14ce
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_manipulation_animateaddition.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_manipulation_expand.png b/example/src/main/res/drawable-xxhdpi/img_manipulation_expand.png
new file mode 100644
index 00000000..b0024b68
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_manipulation_expand.png differ
diff --git a/example/src/main/res/drawable-xxhdpi/img_manipulation_swipetodismiss.png b/example/src/main/res/drawable-xxhdpi/img_manipulation_swipetodismiss.png
new file mode 100644
index 00000000..9af3f560
Binary files /dev/null and b/example/src/main/res/drawable-xxhdpi/img_manipulation_swipetodismiss.png differ
diff --git a/example/res/drawable/card_background_white.9.png b/example/src/main/res/drawable/card_background_white.9.png
similarity index 100%
rename from example/res/drawable/card_background_white.9.png
rename to example/src/main/res/drawable/card_background_white.9.png
diff --git a/example/res/drawable/img_nature1.jpg b/example/src/main/res/drawable/img_nature1.jpg
similarity index 100%
rename from example/res/drawable/img_nature1.jpg
rename to example/src/main/res/drawable/img_nature1.jpg
diff --git a/example/res/drawable/img_nature2.jpg b/example/src/main/res/drawable/img_nature2.jpg
similarity index 100%
rename from example/res/drawable/img_nature2.jpg
rename to example/src/main/res/drawable/img_nature2.jpg
diff --git a/example/res/drawable/img_nature3.jpg b/example/src/main/res/drawable/img_nature3.jpg
similarity index 100%
rename from example/res/drawable/img_nature3.jpg
rename to example/src/main/res/drawable/img_nature3.jpg
diff --git a/example/res/drawable/img_nature4.jpg b/example/src/main/res/drawable/img_nature4.jpg
similarity index 100%
rename from example/res/drawable/img_nature4.jpg
rename to example/src/main/res/drawable/img_nature4.jpg
diff --git a/example/res/drawable/img_nature5.jpg b/example/src/main/res/drawable/img_nature5.jpg
similarity index 100%
rename from example/res/drawable/img_nature5.jpg
rename to example/src/main/res/drawable/img_nature5.jpg
diff --git a/example/src/main/res/layout/activity_dynamiclistview.xml b/example/src/main/res/layout/activity_dynamiclistview.xml
new file mode 100644
index 00000000..4f48b2bf
--- /dev/null
+++ b/example/src/main/res/layout/activity_dynamiclistview.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/res/layout/list_row.xml b/example/src/main/res/layout/activity_dynamiclistview_header.xml
similarity index 54%
rename from example/res/layout/list_row.xml
rename to example/src/main/res/layout/activity_dynamiclistview_header.xml
index a22709c5..de7bf5a3 100644
--- a/example/res/layout/list_row.xml
+++ b/example/src/main/res/layout/activity_dynamiclistview_header.xml
@@ -1,7 +1,9 @@
+
+ android:gravity="center_horizontal"
+ android:padding="16dp"
+ android:text="@string/activity_dynamiclistview_header"
+ android:textSize="16sp" />
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_examples_itemmanipulations.xml b/example/src/main/res/layout/activity_examples_itemmanipulations.xml
new file mode 100644
index 00000000..ea70263e
--- /dev/null
+++ b/example/src/main/res/layout/activity_examples_itemmanipulations.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_expandablelistitem_card.xml b/example/src/main/res/layout/activity_expandablelistitem_card.xml
new file mode 100644
index 00000000..5d07dede
--- /dev/null
+++ b/example/src/main/res/layout/activity_expandablelistitem_card.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_googlecards.xml b/example/src/main/res/layout/activity_googlecards.xml
new file mode 100644
index 00000000..26e26bff
--- /dev/null
+++ b/example/src/main/res/layout/activity_googlecards.xml
@@ -0,0 +1,31 @@
+
+
+
diff --git a/example/src/main/res/layout/activity_googlecards_card.xml b/example/src/main/res/layout/activity_googlecards_card.xml
new file mode 100644
index 00000000..a52f48c7
--- /dev/null
+++ b/example/src/main/res/layout/activity_googlecards_card.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_gridview.xml b/example/src/main/res/layout/activity_gridview.xml
new file mode 100644
index 00000000..6fe9f2c7
--- /dev/null
+++ b/example/src/main/res/layout/activity_gridview.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_main.xml b/example/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..06b73ee8
--- /dev/null
+++ b/example/src/main/res/layout/activity_main.xml
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_mylist.xml b/example/src/main/res/layout/activity_mylist.xml
new file mode 100644
index 00000000..3a685441
--- /dev/null
+++ b/example/src/main/res/layout/activity_mylist.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/activity_stickylistheaders.xml b/example/src/main/res/layout/activity_stickylistheaders.xml
new file mode 100644
index 00000000..bb557404
--- /dev/null
+++ b/example/src/main/res/layout/activity_stickylistheaders.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/list_header.xml b/example/src/main/res/layout/list_header.xml
new file mode 100644
index 00000000..96c4108b
--- /dev/null
+++ b/example/src/main/res/layout/list_header.xml
@@ -0,0 +1,27 @@
+
+
+
diff --git a/example/src/main/res/layout/list_row.xml b/example/src/main/res/layout/list_row.xml
new file mode 100644
index 00000000..94e2fffa
--- /dev/null
+++ b/example/src/main/res/layout/list_row.xml
@@ -0,0 +1,28 @@
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/list_row_draganddrop.xml b/example/src/main/res/layout/list_row_draganddrop.xml
new file mode 100644
index 00000000..b1e59bf6
--- /dev/null
+++ b/example/src/main/res/layout/list_row_draganddrop.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/layout/list_row_dynamiclistview.xml b/example/src/main/res/layout/list_row_dynamiclistview.xml
new file mode 100644
index 00000000..b1e59bf6
--- /dev/null
+++ b/example/src/main/res/layout/list_row_dynamiclistview.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/res/layout/undo_row.xml b/example/src/main/res/layout/undo_row.xml
similarity index 51%
rename from example/res/layout/undo_row.xml
rename to example/src/main/res/layout/undo_row.xml
index b62a48d9..47eddb67 100644
--- a/example/res/layout/undo_row.xml
+++ b/example/src/main/res/layout/undo_row.xml
@@ -1,12 +1,28 @@
-
+
+
+ android:paddingRight="8dp"
+ tools:ignore="overdraw">
\ No newline at end of file
diff --git a/example/src/main/res/menu/menu_expandablelistitem.xml b/example/src/main/res/menu/menu_expandablelistitem.xml
new file mode 100644
index 00000000..141d4aa3
--- /dev/null
+++ b/example/src/main/res/menu/menu_expandablelistitem.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/menu/menu_main.xml b/example/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..354d31bd
--- /dev/null
+++ b/example/src/main/res/menu/menu_main.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values-v19/dimens.xml b/example/src/main/res/values-v19/dimens.xml
new file mode 100644
index 00000000..463322b9
--- /dev/null
+++ b/example/src/main/res/values-v19/dimens.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 73dp
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values/arrays.xml b/example/src/main/res/values/arrays.xml
new file mode 100644
index 00000000..0530f1a7
--- /dev/null
+++ b/example/src/main/res/values/arrays.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ Alpha
+ Left
+ Right
+ Bottom
+ Bottom right
+ Scale
+
+
+
+ Swipe-To-Dismiss
+ Contextual Undo
+ CU - Timed Delete
+
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values/colors.xml b/example/src/main/res/values/colors.xml
new file mode 100644
index 00000000..cb83d64e
--- /dev/null
+++ b/example/src/main/res/values/colors.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+ #3f51b5
+
+ #aa700d39
+ #aa375a1f
+ #aa293576
+ #aa7f3907
+ #aa432677
+
+ #aaff4444
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values/dimens.xml b/example/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..4b87a0ca
--- /dev/null
+++ b/example/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values/strings.xml b/example/src/main/res/values/strings.xml
new file mode 100644
index 00000000..e2766805
--- /dev/null
+++ b/example/src/main/res/values/strings.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ ListViewAnimations
+ Buy me a beer!
+ Buy me a beer!
+ Buy me 2 beers!
+ Buy me 5 beers!
+ Buy me 10 beers!
+ Github
+ Google cards
+ GridView
+ Appearance Animation
+ StickyListHeaders
+ Limit to 2
+ Item Manipulation
+ GridView
+ Drag and Drop
+ DynamicListView
+ Animate addition
+ Expand list items
+ Item deleted
+ Tap to undo
+ Tap on cards to expand or collapse them
+ This is card number %d
+ This is row number %d
+ Header %d
+ This is card %d
+ Thank you!
+ Oops! Something went wrong!
+ \'%1$s\' moved to position %2$s
+ Removed positions: %s
+ Tap on an item to insert a new item
+ This is newly added item %d
+
+ Touch the grip or long press an item to drag.\nSwipe to dismiss.\nTap an item to insert a new item.
+
+
\ No newline at end of file
diff --git a/example/src/main/res/values/styles.xml b/example/src/main/res/values/styles.xml
new file mode 100644
index 00000000..bde65fa5
--- /dev/null
+++ b/example/src/main/res/values/styles.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/generateJavadoc.gradle b/generateJavadoc.gradle
new file mode 100644
index 00000000..616dde2f
--- /dev/null
+++ b/generateJavadoc.gradle
@@ -0,0 +1,52 @@
+buildscript {
+
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'org.ajoberstar:gradle-git:0.8.+'
+ }
+
+}
+
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+import org.ajoberstar.grgit.*
+def repo = Grgit.open(".")
+
+android.libraryVariants.all { variant ->
+ task("generate${variant.name}Javadoc", type: Javadoc) {
+ def branch = repo.branch.current.name
+ if(project.hasProperty('BRANCH')) {
+ branch = BRANCH
+ }
+
+ title = POM_NAME + " (" + branch + ")"
+ description "Generates Javadoc for $variant.name."
+ source = variant.javaCompile.source
+ ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
+ def folder = (branch == 'master' ? VERSION_NAME : branch) + "/" + project.name
+ destinationDir file("./build/docs/javadoc/" + folder)
+ classpath = files(variant.javaCompile.classpath.files) + files(ext.androidJar)
+ options.links("http://docs.oracle.com/javase/7/docs/api/");
+ options.linksOffline("http://d.android.com/reference", System.getenv('ANDROID_HOME') + "/docs/reference")
+ options.setMemberLevel(JavadocMemberLevel.PACKAGE)
+ exclude '**/BuildConfig.java '
+ exclude '**/R.java'
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 7dda3478..d1c211df 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,24 @@
-VERSION_NAME=2.6.0
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+nexusUsername=DUMMY
+nexusPassword=DUMMY
+isRelease=false
+
+VERSION_NAME=3.0.0
GROUP=com.nhaarman.listviewanimations
POM_DESCRIPTION=ListViewAnimations library
@@ -10,4 +30,9 @@ POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=nhaarman
-POM_DEVELOPER_NAME=Niek Haarman
\ No newline at end of file
+POM_DEVELOPER_NAME=Niek Haarman
+
+# Add dummy passwords
+nhaarman_lva_storePassword=DUMMY
+nhaarman_lva_keyAlias=DUMMY
+nhaarman_lva_keyPassword=DUMMY
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..8c0fb64a
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..37e9a177
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Jun 14 10:45:24 CEST 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 00000000..91a7e269
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/lib-core-slh/.classpath b/lib-core-slh/.classpath
new file mode 100644
index 00000000..3d27b6d6
--- /dev/null
+++ b/lib-core-slh/.classpath
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib-core-slh/.project b/lib-core-slh/.project
new file mode 100644
index 00000000..c71d5ef1
--- /dev/null
+++ b/lib-core-slh/.project
@@ -0,0 +1,39 @@
+
+
+ ListViewAnimations (StickyListHeaders Library)
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/lib-core-slh/build.gradle b/lib-core-slh/build.gradle
new file mode 100644
index 00000000..9c250e65
--- /dev/null
+++ b/lib-core-slh/build.gradle
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'github-pages'
+
+apply from: '../pushMaven.gradle'
+apply from: '../generateJavadoc.gradle'
+apply from: '../publishGhPages.gradle'
+
+dependencies {
+ compile project(':lib-core')
+
+ compile 'se.emilsjolander:stickylistheaders:2.5.0'
+
+ /* Test libraries */
+ androidTestCompile ('junit:junit:4.11') { exclude group: 'org.hamcrest' }
+ androidTestCompile('org.mockito:mockito-core:1.9.5') { exclude group: 'org.hamcrest' }
+ androidTestCompile 'org.hamcrest:hamcrest-all:1.3'
+ androidTestCompile files('libs/dexmaker-1.1.jar')
+ androidTestCompile files('libs/dexmaker-mockito-1.1-custom.jar')
+}
+
+android {
+ compileSdkVersion 20
+ buildToolsVersion '20.0.0'
+
+ defaultConfig {
+ minSdkVersion 8
+ //noinspection OldTargetApi
+ targetSdkVersion 20
+ versionName project.VERSION_NAME
+ versionCode Integer.parseInt(new Date().format('yyyyMMddHH'))
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+
+ packagingOptions {
+ exclude 'LICENSE.txt'
+ }
+
+ lintOptions {
+ textReport true
+ textOutput 'stdout'
+ warningsAsErrors true
+ }
+}
\ No newline at end of file
diff --git a/lib-core-slh/gradle.properties b/lib-core-slh/gradle.properties
new file mode 100644
index 00000000..5a906f9c
--- /dev/null
+++ b/lib-core-slh/gradle.properties
@@ -0,0 +1,19 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+POM_NAME=ListViewAnimations Library
+POM_ARTIFACT_ID=lib-core-slh
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lib-core-slh/libs/dexmaker-1.1.jar b/lib-core-slh/libs/dexmaker-1.1.jar
new file mode 100644
index 00000000..52a1a188
Binary files /dev/null and b/lib-core-slh/libs/dexmaker-1.1.jar differ
diff --git a/lib-core-slh/libs/dexmaker-mockito-1.1-custom.jar b/lib-core-slh/libs/dexmaker-mockito-1.1-custom.jar
new file mode 100644
index 00000000..adb688b1
Binary files /dev/null and b/lib-core-slh/libs/dexmaker-mockito-1.1-custom.jar differ
diff --git a/lib-core-slh/lint.xml b/lib-core-slh/lint.xml
new file mode 100644
index 00000000..65906e35
--- /dev/null
+++ b/lib-core-slh/lint.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core-slh/pom.xml b/lib-core-slh/pom.xml
new file mode 100644
index 00000000..e9ddc5a7
--- /dev/null
+++ b/lib-core-slh/pom.xml
@@ -0,0 +1,207 @@
+
+
+
+ 4.0.0
+
+
+ com.nhaarman.listviewanimations
+ parent
+ 3.0.0-SNAPSHOT
+
+
+ lib-core-slh
+ apklib
+
+ ListViewAnimations (StickyListHeaders Library)
+
+
+
+
+ com.google.android
+ android
+ provided
+
+
+
+
+ com.nhaarman.listviewanimations
+ lib-core
+ ${project.version}
+ apklib
+
+
+
+
+ se.emilsjolander
+ stickylistheaders
+ aar
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+ src/androidTest/java
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ ${project.basedir}/src/main/AndroidManifest.xml
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ package
+
+ attach-artifact
+
+
+
+
+ jar
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+
+ android-maven-plugin
+ [3.8.2,)
+
+ consume-aar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ release
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+ verify
+
+ package
+ true
+
+
+ ${project.build.directory}/${project.artifactId}.apklib
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core-slh/proguard-project.txt b/lib-core-slh/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/lib-core-slh/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/library/project.properties b/lib-core-slh/project.properties
similarity index 53%
rename from library/project.properties
rename to lib-core-slh/project.properties
index 6148244d..7fcd9532 100644
--- a/library/project.properties
+++ b/lib-core-slh/project.properties
@@ -1,3 +1,19 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
diff --git a/lib-core-slh/src/main/AndroidManifest.xml b/lib-core-slh/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a31201a8
--- /dev/null
+++ b/lib-core-slh/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/appearance/StickyListHeadersAdapterDecorator.java b/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/appearance/StickyListHeadersAdapterDecorator.java
new file mode 100644
index 00000000..3cde47f5
--- /dev/null
+++ b/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/appearance/StickyListHeadersAdapterDecorator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.util.AnimatorUtil;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nhaarman.listviewanimations.util.StickyListHeadersListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+
+/**
+ * A {@link com.nhaarman.listviewanimations.BaseAdapterDecorator} which can be used to animate header views provided by a
+ * {@link se.emilsjolander.stickylistheaders.StickyListHeadersAdapter}.
+ */
+public class StickyListHeadersAdapterDecorator extends BaseAdapterDecorator implements StickyListHeadersAdapter {
+
+ /**
+ * Alpha property.
+ */
+ private static final String ALPHA = "alpha";
+
+ /**
+ * The decorated {@link se.emilsjolander.stickylistheaders.StickyListHeadersAdapter}.
+ */
+ @NonNull
+ private final StickyListHeadersAdapter mStickyListHeadersAdapter;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.appearance.ViewAnimator} responsible for animating the Views.
+ */
+ @Nullable
+ private ViewAnimator mViewAnimator;
+
+ /**
+ * Create a new {@code StickyListHeadersAdapterDecorator}, decorating given {@link android.widget.BaseAdapter}.
+ *
+ * @param baseAdapter the {@code BaseAdapter} to decorate. If this is a {@code BaseAdapterDecorator}, it should wrap an instance of
+ * {@link se.emilsjolander.stickylistheaders.StickyListHeadersAdapter}.
+ */
+ public StickyListHeadersAdapterDecorator(@NonNull final BaseAdapter baseAdapter) {
+ super(baseAdapter);
+
+ BaseAdapter adapter = baseAdapter;
+ while (adapter instanceof BaseAdapterDecorator) {
+ adapter = ((BaseAdapterDecorator) adapter).getDecoratedBaseAdapter();
+ }
+
+ if (!(adapter instanceof StickyListHeadersAdapter)) {
+ throw new IllegalArgumentException(adapter.getClass().getCanonicalName() + " does not implement StickyListHeadersAdapter");
+ }
+
+ mStickyListHeadersAdapter = (StickyListHeadersAdapter) adapter;
+ }
+
+ /**
+ * Sets the {@link se.emilsjolander.stickylistheaders.StickyListHeadersListView} that this adapter will be bound to.
+ */
+ public void setStickyListHeadersListView(@NonNull final StickyListHeadersListView listView) {
+ ListViewWrapper stickyListHeadersListViewWrapper = new StickyListHeadersListViewWrapper(listView);
+ setListViewWrapper(stickyListHeadersListViewWrapper);
+ }
+
+ /**
+ * Returns the {@link com.nhaarman.listviewanimations.appearance.ViewAnimator} responsible for animating the header Views in this adapter.
+ */
+ @Nullable
+ public ViewAnimator getViewAnimator() {
+ return mViewAnimator;
+ }
+
+ @Override
+ public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) {
+ super.setListViewWrapper(listViewWrapper);
+ mViewAnimator = new ViewAnimator(listViewWrapper);
+ }
+
+ @Override
+ public View getHeaderView(final int position, final View convertView, final ViewGroup parent) {
+ if (getListViewWrapper() == null) {
+ throw new IllegalStateException("Call setStickyListHeadersListView() on this AnimationAdapter first!");
+ }
+
+ if (convertView != null) {
+ assert mViewAnimator != null;
+ mViewAnimator.cancelExistingAnimation(convertView);
+ }
+
+ View itemView = mStickyListHeadersAdapter.getHeaderView(position, convertView, parent);
+
+ animateViewIfNecessary(position, itemView, parent);
+ return itemView;
+ }
+
+ /**
+ * Animates given View if necessary.
+ *
+ * @param position the position of the item the View represents.
+ * @param view the View that should be animated.
+ * @param parent the parent the View is hosted in.
+ */
+ private void animateViewIfNecessary(final int position, @NonNull final View view, @NonNull final ViewGroup parent) {
+ Animator[] childAnimators;
+ if (getDecoratedBaseAdapter() instanceof AnimationAdapter) {
+ childAnimators = ((AnimationAdapter) getDecoratedBaseAdapter()).getAnimators(parent, view);
+ } else {
+ childAnimators = new Animator[0];
+ }
+ Animator alphaAnimator = ObjectAnimator.ofFloat(view, ALPHA, 0, 1);
+
+ assert mViewAnimator != null;
+ mViewAnimator.animateViewIfNecessary(position, view, AnimatorUtil.concatAnimators(childAnimators, new Animator[0], alphaAnimator));
+ }
+
+ @Override
+ public long getHeaderId(final int position) {
+ return mStickyListHeadersAdapter.getHeaderId(position);
+ }
+}
diff --git a/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/util/StickyListHeadersListViewWrapper.java b/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/util/StickyListHeadersListViewWrapper.java
new file mode 100644
index 00000000..e12ad6b4
--- /dev/null
+++ b/lib-core-slh/src/main/java/com/nhaarman/listviewanimations/util/StickyListHeadersListViewWrapper.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.ListAdapter;
+
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+
+public class StickyListHeadersListViewWrapper implements ListViewWrapper {
+
+ @NonNull
+ private final StickyListHeadersListView mListView;
+
+ public StickyListHeadersListViewWrapper(@NonNull final StickyListHeadersListView listView) {
+ mListView = listView;
+ }
+
+ @NonNull
+ @Override
+ public StickyListHeadersListView getListView() {
+ return mListView;
+ }
+
+ @Nullable
+ @Override
+ public View getChildAt(final int index) {
+ return mListView.getChildAt(index);
+ }
+
+ @Override
+ public int getFirstVisiblePosition() {
+ return mListView.getFirstVisiblePosition();
+ }
+
+ @Override
+ public int getLastVisiblePosition() {
+ return mListView.getLastVisiblePosition();
+ }
+
+ @Override
+ public int getCount() {
+ return mListView.getCount();
+ }
+
+ @Override
+ public int getChildCount() {
+ return mListView.getChildCount();
+ }
+
+ @Override
+ public int getHeaderViewsCount() {
+ return mListView.getHeaderViewsCount();
+ }
+
+ @Override
+ public int getPositionForView(@NonNull final View view) {
+ return mListView.getPositionForView(view);
+ }
+
+ @NonNull
+ @Override
+ public ListAdapter getAdapter() {
+ return mListView.getAdapter();
+ }
+
+ @Override
+ public void smoothScrollBy(final int distance, final int duration) {
+ mListView.smoothScrollBy(distance, duration);
+ }
+}
diff --git a/lib-core/.classpath b/lib-core/.classpath
new file mode 100644
index 00000000..3d27b6d6
--- /dev/null
+++ b/lib-core/.classpath
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib-core/.project b/lib-core/.project
new file mode 100644
index 00000000..9473be01
--- /dev/null
+++ b/lib-core/.project
@@ -0,0 +1,39 @@
+
+
+ ListViewAnimations (Core Library)
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/lib-core/build.gradle b/lib-core/build.gradle
new file mode 100644
index 00000000..6e6aa8cb
--- /dev/null
+++ b/lib-core/build.gradle
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'github-pages'
+
+apply from: '../pushMaven.gradle'
+apply from: '../generateJavadoc.gradle'
+apply from: '../publishGhPages.gradle'
+
+dependencies {
+ compile 'com.nineoldandroids:library:2.4.0'
+ compile 'com.android.support:support-annotations:+'
+
+ /* Test libraries */
+ androidTestCompile ('org.mockito:mockito-core:1.9.5') { exclude group: 'org.hamcrest' }
+ androidTestCompile ('junit:junit:4.11') { exclude group: 'org.hamcrest' }
+ androidTestCompile 'org.hamcrest:hamcrest-all:1.3'
+ androidTestCompile files('libs/dexmaker-1.1.jar')
+ androidTestCompile files('libs/dexmaker-mockito-1.1-custom.jar')
+}
+
+android {
+ compileSdkVersion 20
+ buildToolsVersion '20.0.0'
+
+ defaultConfig {
+ minSdkVersion 8
+ //noinspection OldTargetApi
+ targetSdkVersion 20
+ versionName project.VERSION_NAME
+ versionCode Integer.parseInt(new Date().format('yyyyMMddHH'))
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+
+ packagingOptions {
+ exclude 'LICENSE.txt'
+ }
+
+ lintOptions {
+ textReport true
+ textOutput 'stdout'
+ warningsAsErrors true
+ }
+}
\ No newline at end of file
diff --git a/lib-core/gradle.properties b/lib-core/gradle.properties
new file mode 100644
index 00000000..df194480
--- /dev/null
+++ b/lib-core/gradle.properties
@@ -0,0 +1,19 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+POM_NAME=ListViewAnimations Library
+POM_ARTIFACT_ID=lib-core
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lib-core/libs/dexmaker-1.1.jar b/lib-core/libs/dexmaker-1.1.jar
new file mode 100644
index 00000000..52a1a188
Binary files /dev/null and b/lib-core/libs/dexmaker-1.1.jar differ
diff --git a/lib-core/libs/dexmaker-mockito-1.1-custom.jar b/lib-core/libs/dexmaker-mockito-1.1-custom.jar
new file mode 100644
index 00000000..adb688b1
Binary files /dev/null and b/lib-core/libs/dexmaker-mockito-1.1-custom.jar differ
diff --git a/lib-core/lint.xml b/lib-core/lint.xml
new file mode 100644
index 00000000..65906e35
--- /dev/null
+++ b/lib-core/lint.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core/pom.xml b/lib-core/pom.xml
new file mode 100644
index 00000000..e69aa70a
--- /dev/null
+++ b/lib-core/pom.xml
@@ -0,0 +1,202 @@
+
+
+
+ 4.0.0
+
+
+ com.nhaarman.listviewanimations
+ parent
+ 3.0.0-SNAPSHOT
+
+
+ lib-core
+ apklib
+
+ ListViewAnimations (Core Library)
+
+
+
+
+ com.google.android
+ android
+ provided
+
+
+ com.android.support
+ support-annotations
+ jar
+
+
+
+
+ com.nineoldandroids
+ library
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+ src/androidTest/java
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ ${project.basedir}/src/main/AndroidManifest.xml
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ package
+
+ attach-artifact
+
+
+
+
+ jar
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+
+ android-maven-plugin
+ [3.8.2,)
+
+ consume-aar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ release
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+ verify
+
+ package
+ true
+
+
+ ${project.build.directory}/${project.artifactId}.apklib
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core/proguard-project.txt b/lib-core/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/lib-core/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/lib-core/project.properties b/lib-core/project.properties
new file mode 100644
index 00000000..7fcd9532
--- /dev/null
+++ b/lib-core/project.properties
@@ -0,0 +1,32 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
+android.library.reference.1=../../../workspace/android-support-v7-appcompat
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/ArrayAdapterTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/ArrayAdapterTest.java
new file mode 100644
index 00000000..ffc29483
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/ArrayAdapterTest.java
@@ -0,0 +1,138 @@
+package com.nhaarman.listviewanimations;
+
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+
+@SuppressWarnings("ConstantNamingConvention")
+public class ArrayAdapterTest extends TestCase {
+
+ private static final String A = "A";
+
+ private static final String B = "B";
+
+ private static final String C = "C";
+
+ private static final String D = "D";
+
+ private List mItems;
+
+ private ArrayAdapter mArrayAdapter;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mItems = new ArrayList<>();
+ mItems.addAll(Arrays.asList(A, B, C));
+ mArrayAdapter = new TestArrayAdapter(mItems);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mItems = null;
+ mArrayAdapter = null;
+ }
+
+ public void testNoArgCreation() {
+ TestArrayAdapter testArrayAdapter = new TestArrayAdapter();
+ assertThat(testArrayAdapter.getCount(), is(0));
+ }
+
+ public void testEmptyCreation() {
+ List items = new ArrayList<>();
+ TestArrayAdapter testArrayAdapter = new TestArrayAdapter(items);
+
+ assertThat(testArrayAdapter.getCount(), is(0));
+ assertThat(testArrayAdapter.getItems(), is(items));
+ }
+
+ public void testNonEmptyCreation() {
+ TestArrayAdapter testArrayAdapter = new TestArrayAdapter(mItems);
+
+ assertThat(testArrayAdapter.getCount(), is(3));
+ assertThat(testArrayAdapter.getItems(), is(mItems));
+ }
+
+ public void testGetItem() {
+ assertThat(mArrayAdapter.getItem(0), is(A));
+ assertThat(mArrayAdapter.getItem(1), is(B));
+ assertThat(mArrayAdapter.getItem(2), is(C));
+ }
+
+ public void testAdd() {
+ mArrayAdapter.add(D);
+
+ assertThat(mArrayAdapter.getCount(), is(4));
+ assertThat(mArrayAdapter.getItem(3), is(D));
+ }
+
+ public void testAddIndex() {
+ mArrayAdapter.add(1, D);
+
+ assertThat(mArrayAdapter.getCount(), is(4));
+ assertThat(mArrayAdapter.getItem(0), is(A));
+ assertThat(mArrayAdapter.getItem(1), is(D));
+ assertThat(mArrayAdapter.getItem(2), is(B));
+ }
+
+ public void testContains() {
+ assertThat(mArrayAdapter.contains(A), is(true));
+ assertThat(mArrayAdapter.contains(D), is(false));
+ }
+
+ public void testClear() {
+ mArrayAdapter.clear();
+
+ assertThat(mArrayAdapter.getCount(), is(0));
+ assertThat(mItems.size(), is(0));
+ }
+
+ public void testRemoveObject() {
+ mArrayAdapter.remove(A);
+
+ assertThat(mArrayAdapter.getCount(), is(2));
+ assertThat(mArrayAdapter.getItem(0), is(B));
+ }
+
+ public void testRemovePosition() {
+ mArrayAdapter.remove(0);
+
+ assertThat(mArrayAdapter.getCount(), is(2));
+ assertThat(mArrayAdapter.getItem(0), is(B));
+ }
+
+ public void testSwapItems() {
+ mArrayAdapter.swapItems(0, 1);
+
+ assertThat(mArrayAdapter.getCount(), is(3));
+ assertThat(mArrayAdapter.getItem(0), is(B));
+ assertThat(mArrayAdapter.getItem(1), is(A));
+ assertThat(mArrayAdapter.getItem(2), is(C));
+ }
+
+ private static class TestArrayAdapter extends ArrayAdapter {
+
+ private TestArrayAdapter() {
+ }
+
+ private TestArrayAdapter(@Nullable final List objects) {
+ super(objects);
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ //noinspection ConstantConditions
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/BaseAdapterDecoratorTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/BaseAdapterDecoratorTest.java
new file mode 100644
index 00000000..35372c78
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/BaseAdapterDecoratorTest.java
@@ -0,0 +1,200 @@
+package com.nhaarman.listviewanimations;
+
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+
+import com.nhaarman.listviewanimations.util.AbsListViewWrapper;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+
+import junit.framework.TestCase;
+
+import org.mockito.*;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.*;
+
+@SuppressWarnings({"AnonymousInnerClass", "EmptyClass", "ConstantConditions"})
+public class BaseAdapterDecoratorTest extends TestCase {
+
+ private BaseAdapterDecorator mBaseAdapterDecorator;
+
+ private BaseAdapter mBaseAdapter;
+
+ @Mock
+ private View mView;
+
+ @Mock
+ private ListView mAbsListView;
+
+ @Mock
+ private ListViewWrapper mListViewWrapper;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ mBaseAdapter = spy(new BaseAdapterImpl());
+ mBaseAdapterDecorator = new BaseAdapterDecorator(mBaseAdapter) {
+ };
+ }
+
+ public void testGetDecoratedBaseAdapter() {
+ assertThat(mBaseAdapterDecorator.getDecoratedBaseAdapter(), is(equalTo(mBaseAdapter)));
+ }
+
+ public void testGetRootAdapter() {
+ assertThat(mBaseAdapterDecorator.getRootAdapter(), is(equalTo(mBaseAdapter)));
+ }
+
+ public void testGetDoubleWrappedAdapters() {
+ BaseAdapterDecorator doubleWrappedAdapter = new BaseAdapterDecorator(mBaseAdapterDecorator) {
+ };
+ assertThat(doubleWrappedAdapter.getDecoratedBaseAdapter(), is(equalTo((BaseAdapter) mBaseAdapterDecorator)));
+ assertThat(doubleWrappedAdapter.getRootAdapter(), is(equalTo(mBaseAdapter)));
+ }
+
+ public void testSetAbsListView() {
+ mBaseAdapterDecorator.setAbsListView(mAbsListView);
+
+ assertThat(mBaseAdapterDecorator.getListViewWrapper(), instanceOf(AbsListViewWrapper.class));
+ }
+
+ public void testSetListViewWrapper() {
+ mBaseAdapterDecorator.setListViewWrapper(mListViewWrapper);
+
+ assertThat(mBaseAdapterDecorator.getListViewWrapper(), is(mListViewWrapper));
+ }
+
+ public void testGetCount() {
+ when(mBaseAdapter.getCount()).thenReturn(5);
+
+ assertThat(mBaseAdapterDecorator.getCount(), is(5));
+ }
+
+ public void testGetItem() {
+ Object value = new Object();
+ when(mBaseAdapter.getItem(anyInt())).thenReturn(value);
+
+ assertThat(mBaseAdapterDecorator.getItem(0), is(value));
+ }
+
+ public void testGetItemId() {
+ when(mBaseAdapter.getItemId(anyInt())).thenReturn(5L);
+
+ assertThat(mBaseAdapterDecorator.getItemId(0), is(5L));
+ }
+
+ public void testGetView() {
+ when(mBaseAdapter.getView(anyInt(), any(View.class), any(ViewGroup.class))).thenReturn(mView);
+
+ assertThat(mBaseAdapterDecorator.getView(0, null, null), is(mView));
+ }
+
+ public void testAreAllItemsEnabled() {
+ when(mBaseAdapter.areAllItemsEnabled()).thenReturn(true);
+ assertThat(mBaseAdapterDecorator.areAllItemsEnabled(), is(true));
+
+ when(mBaseAdapter.areAllItemsEnabled()).thenReturn(false);
+ assertThat(mBaseAdapterDecorator.areAllItemsEnabled(), is(false));
+ }
+
+ public void testGetDropDownView() {
+ when(mBaseAdapter.getDropDownView(anyInt(), any(View.class), any(ViewGroup.class))).thenReturn(mView);
+
+ assertThat(mBaseAdapterDecorator.getDropDownView(0, null, null), is(mView));
+ }
+
+ public void testGetItemViewType() {
+ when(mBaseAdapter.getItemViewType(anyInt())).thenReturn(5);
+
+ assertThat(mBaseAdapterDecorator.getItemViewType(0), is(5));
+ }
+
+ public void testGetViewTypeCount() {
+ when(mBaseAdapter.getViewTypeCount()).thenReturn(5);
+
+ assertThat(mBaseAdapterDecorator.getViewTypeCount(), is(5));
+ }
+
+ public void testHasStableIds() {
+ when(mBaseAdapter.hasStableIds()).thenReturn(true);
+ assertThat(mBaseAdapterDecorator.hasStableIds(), is(true));
+
+ when(mBaseAdapter.hasStableIds()).thenReturn(false);
+ assertThat(mBaseAdapterDecorator.hasStableIds(), is(false));
+ }
+
+ public void testIsEmpty() {
+ when(mBaseAdapter.isEmpty()).thenReturn(true);
+ assertThat(mBaseAdapterDecorator.isEmpty(), is(true));
+
+ when(mBaseAdapter.isEmpty()).thenReturn(false);
+ assertThat(mBaseAdapterDecorator.isEmpty(), is(false));
+ }
+
+ public void testIsEnabled() {
+ when(mBaseAdapter.isEnabled(anyInt())).thenReturn(true);
+ assertThat(mBaseAdapterDecorator.isEnabled(0), is(true));
+
+ when(mBaseAdapter.isEnabled(anyInt())).thenReturn(false);
+ assertThat(mBaseAdapterDecorator.isEnabled(0), is(false));
+ }
+
+ public void testNotifyDataSetChanged() {
+ mBaseAdapterDecorator.notifyDataSetChanged();
+
+ verify(mBaseAdapter).notifyDataSetChanged();
+ }
+
+ public void testNotifyDataSetInvalidated() {
+ mBaseAdapterDecorator.notifyDataSetInvalidated();
+
+ verify(mBaseAdapter).notifyDataSetInvalidated();
+ }
+
+ public void testRegisterDataSetObserver() {
+ DataSetObserver mock = mock(DataSetObserver.class);
+ mBaseAdapterDecorator.registerDataSetObserver(mock);
+
+ verify(mBaseAdapter).registerDataSetObserver(mock);
+ }
+
+ public void testUnregisterDataSetObserver() {
+ DataSetObserver mock = mock(DataSetObserver.class);
+ mBaseAdapterDecorator.registerDataSetObserver(mock);
+ mBaseAdapterDecorator.unregisterDataSetObserver(mock);
+
+ verify(mBaseAdapter).unregisterDataSetObserver(mock);
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ protected static class BaseAdapterImpl extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return 0;
+ }
+
+ @Override
+ public Object getItem(final int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/appearance/ViewAnimatorTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/appearance/ViewAnimatorTest.java
new file mode 100644
index 00000000..ec8b23af
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/appearance/ViewAnimatorTest.java
@@ -0,0 +1,102 @@
+package com.nhaarman.listviewanimations.appearance;
+
+import android.test.InstrumentationTestCase;
+import android.view.View;
+
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+
+import org.mockito.Mock;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.MockitoAnnotations.*;
+
+@SuppressWarnings({"MagicNumber", "AnonymousInnerClass"})
+public class ViewAnimatorTest extends InstrumentationTestCase {
+
+ private ViewAnimator mViewAnimator;
+
+ @Mock
+ private ListViewWrapper mListViewWrapper;
+
+ @Mock
+ private View mView;
+
+ @Mock
+ private Animator mAnimator;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ initMocks(this);
+
+ mViewAnimator = new ViewAnimator(mListViewWrapper);
+ }
+
+ public void testFirstViewAnimated() {
+ getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mViewAnimator.animateViewIfNecessary(0, mView, new Animator[]{mAnimator});
+ }
+ }
+ );
+
+ verify(mAnimator, timeout(500)).start();
+ }
+
+ public void testSecondViewAnimated() throws InterruptedException {
+ mViewAnimator.setAnimationDelayMillis(500);
+
+ getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mViewAnimator.animateViewIfNecessary(0, mView, new Animator[]{mAnimator});
+ mViewAnimator.animateViewIfNecessary(1, mView, new Animator[]{mAnimator});
+ }
+ }
+ );
+
+ verify(mAnimator, timeout(500)).start();
+ reset(mAnimator);
+ Thread.sleep(100);
+ verify(mAnimator, never()).start();
+ verify(mAnimator, timeout(500)).start();
+ }
+
+ public void testDisabledAnimations() throws InterruptedException {
+ mViewAnimator.disableAnimations();
+ getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mViewAnimator.animateViewIfNecessary(0, mView, new Animator[]{mAnimator});
+ mViewAnimator.animateViewIfNecessary(1, mView, new Animator[]{mAnimator});
+ }
+ }
+ );
+
+ Thread.sleep(10000);
+
+ verify(mAnimator, never()).start();
+ }
+
+ public void testSetAnimationDuration() {
+ mViewAnimator.setAnimationDurationMillis(500);
+ getInstrumentation().runOnMainSync(
+ new Runnable() {
+ @Override
+ public void run() {
+ mViewAnimator.animateViewIfNecessary(0, mView, new Animator[]{mAnimator});
+ }
+ }
+ );
+
+ verify(mAnimator, timeout(1000)).setDuration(500);
+
+ }
+
+}
\ No newline at end of file
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AbsListViewWrapperTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AbsListViewWrapperTest.java
new file mode 100644
index 00000000..707b9077
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AbsListViewWrapperTest.java
@@ -0,0 +1,117 @@
+package com.nhaarman.listviewanimations.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import junit.framework.TestCase;
+
+import org.mockito.*;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.*;
+
+public class AbsListViewWrapperTest extends TestCase {
+
+ private AbsListViewWrapper mAbsListViewWrapper;
+
+ @Mock
+ private AbsListViewImpl mAbsListView;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+ mAbsListViewWrapper = new AbsListViewWrapper(mAbsListView);
+ }
+
+ public void testGetListView() {
+ assertThat(mAbsListViewWrapper.getListView(), is((AbsListView) mAbsListView));
+ }
+
+ public void testGetChildAt() {
+ View mock = mock(View.class);
+ when(mAbsListView.getChildAt(anyInt())).thenReturn(mock);
+
+ assertThat(mAbsListViewWrapper.getChildAt(0), is(mock));
+ }
+
+ public void testGetFirstVisiblePosition() {
+ when(mAbsListView.getFirstVisiblePosition()).thenReturn(5);
+
+ assertThat(mAbsListViewWrapper.getFirstVisiblePosition(), is(5));
+ }
+
+ public void testGetLastVisiblePosition() {
+ when(mAbsListView.getLastVisiblePosition()).thenReturn(5);
+
+ assertThat(mAbsListViewWrapper.getLastVisiblePosition(), is(5));
+ }
+
+ public void testGetCount() {
+ when(mAbsListView.getCount()).thenReturn(5);
+
+ assertThat(mAbsListView.getCount(), is(5));
+ }
+
+ public void testGetChildCount() {
+ when(mAbsListView.getChildCount()).thenReturn(5);
+
+ assertThat(mAbsListView.getChildCount(), is(5));
+ }
+
+ public void testGetHeaderViewsCount() {
+ assertThat(mAbsListViewWrapper.getHeaderViewsCount(), is(0));
+ }
+
+
+ public void testListViewGetHeaderViewsCount() {
+ ListView listView = mock(ListView.class);
+ mAbsListViewWrapper = new AbsListViewWrapper(listView);
+
+ when(listView.getHeaderViewsCount()).thenReturn(5);
+ assertThat(mAbsListViewWrapper.getHeaderViewsCount(), is(5));
+ }
+
+ public void testGetPositionForView() {
+ View mock = mock(View.class);
+ when(mAbsListView.getPositionForView(any(View.class))).thenReturn(5);
+
+ assertThat(mAbsListViewWrapper.getPositionForView(mock), is(5));
+ }
+
+ public void testGetAdapter() {
+ ListAdapter mock = mock(ListAdapter.class);
+ when(mAbsListView.getAdapter()).thenReturn(mock);
+
+ assertThat(mAbsListViewWrapper.getAdapter(), is(mock));
+ }
+
+ public void testSmoothScrollBy() {
+ mAbsListViewWrapper.smoothScrollBy(4, 5);
+
+ verify(mAbsListView).smoothScrollBy(4, 5);
+ }
+
+ protected abstract static class AbsListViewImpl extends AbsListView {
+
+ public AbsListViewImpl(final Context context) {
+ super(context);
+ }
+
+ public AbsListViewImpl(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AbsListViewImpl(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AdapterViewUtilTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AdapterViewUtilTest.java
new file mode 100644
index 00000000..fb5d6dd7
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AdapterViewUtilTest.java
@@ -0,0 +1,169 @@
+package com.nhaarman.listviewanimations.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ListView;
+
+import junit.framework.TestCase;
+
+import org.mockito.*;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.*;
+
+public class AdapterViewUtilTest extends TestCase {
+
+ private static final int POSITION = 5;
+
+ @Mock
+ private ListViewWrapper mListViewWrapper;
+
+ @Mock
+ private AbsListViewImpl mAbsListView;
+
+ @Mock
+ private ListView mListView;
+
+ @Mock
+ private View mView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+ }
+
+ public void testListViewWrapperGetPositionForView() throws Exception {
+ when(mListViewWrapper.getPositionForView(any(View.class))).thenReturn(5);
+ when(mListViewWrapper.getHeaderViewsCount()).thenReturn(0);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListViewWrapper, mView), is(5));
+ }
+
+ public void testListViewWrapperGetPositionForViewWithHeaderViews() throws Exception {
+ when(mListViewWrapper.getPositionForView(any(View.class))).thenReturn(5);
+ when(mListViewWrapper.getHeaderViewsCount()).thenReturn(2);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListViewWrapper, mView), is(3));
+ }
+
+ public void testAbsListViewGetPositionForView() throws Exception {
+ when(mAbsListView.getPositionForView(any(View.class))).thenReturn(5);
+
+ assertThat(AdapterViewUtil.getPositionForView(mAbsListView, mView), is(5));
+ }
+
+ public void testListViewGetPositionForView() throws Exception {
+ when(mListView.getPositionForView(any(View.class))).thenReturn(5);
+ when(mListView.getHeaderViewsCount()).thenReturn(0);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListView, mView), is(5));
+ }
+
+ public void testListViewGetPositionForViewWithHeaderViews() throws Exception {
+ when(mListView.getPositionForView(any(View.class))).thenReturn(5);
+ when(mListView.getHeaderViewsCount()).thenReturn(2);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListView, mView), is(3));
+ }
+
+ public void testListViewWrapperGetViewForPosition() throws Exception {
+ View dummyView1 = mock(View.class);
+ View dummyView2 = mock(View.class);
+
+ when(mListViewWrapper.getChildAt(0)).thenReturn(dummyView1);
+ when(mListViewWrapper.getChildAt(1)).thenReturn(mView);
+ when(mListViewWrapper.getChildAt(2)).thenReturn(dummyView2);
+
+ when(mListViewWrapper.getPositionForView(dummyView1)).thenReturn(POSITION - 1);
+ when(mListViewWrapper.getPositionForView(mView)).thenReturn(POSITION);
+ when(mListViewWrapper.getPositionForView(dummyView2)).thenReturn(POSITION + 1);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListViewWrapper, mView), is(POSITION));
+ }
+
+ public void testListViewWrapperGetViewForPositionWithHeaderViews() throws Exception {
+ View dummyView1 = mock(View.class);
+ View dummyView2 = mock(View.class);
+
+ when(mListViewWrapper.getChildAt(0)).thenReturn(dummyView1);
+ when(mListViewWrapper.getChildAt(1)).thenReturn(mView);
+ when(mListViewWrapper.getChildAt(2)).thenReturn(dummyView2);
+
+ when(mListViewWrapper.getPositionForView(dummyView1)).thenReturn(POSITION - 1);
+ when(mListViewWrapper.getPositionForView(mView)).thenReturn(POSITION);
+ when(mListViewWrapper.getPositionForView(dummyView2)).thenReturn(POSITION + 1);
+
+ when(mListViewWrapper.getHeaderViewsCount()).thenReturn(2);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListViewWrapper, mView), is(POSITION - 2));
+ }
+
+ public void testAbsListViewGetViewForPosition() throws Exception {
+ View dummyView1 = mock(View.class);
+ View dummyView2 = mock(View.class);
+
+ when(mAbsListView.getChildAt(0)).thenReturn(dummyView1);
+ when(mAbsListView.getChildAt(1)).thenReturn(mView);
+ when(mAbsListView.getChildAt(2)).thenReturn(dummyView2);
+
+ when(mAbsListView.getPositionForView(dummyView1)).thenReturn(POSITION - 1);
+ when(mAbsListView.getPositionForView(mView)).thenReturn(POSITION);
+ when(mAbsListView.getPositionForView(dummyView2)).thenReturn(POSITION + 1);
+
+ assertThat(AdapterViewUtil.getPositionForView(mAbsListView, mView), is(POSITION));
+ }
+
+ public void testListViewGetViewForPosition() throws Exception {
+ View dummyView1 = mock(View.class);
+ View dummyView2 = mock(View.class);
+
+ when(mListView.getChildAt(0)).thenReturn(dummyView1);
+ when(mListView.getChildAt(1)).thenReturn(mView);
+ when(mListView.getChildAt(2)).thenReturn(dummyView2);
+
+ when(mListView.getPositionForView(dummyView1)).thenReturn(POSITION - 1);
+ when(mListView.getPositionForView(mView)).thenReturn(POSITION);
+ when(mListView.getPositionForView(dummyView2)).thenReturn(POSITION + 1);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListView, mView), is(POSITION));
+ }
+
+ public void testListViewGetViewForPositionWithHeaderViews() throws Exception {
+ View dummyView1 = mock(View.class);
+ View dummyView2 = mock(View.class);
+
+ when(mListView.getChildAt(0)).thenReturn(dummyView1);
+ when(mListView.getChildAt(1)).thenReturn(mView);
+ when(mListView.getChildAt(2)).thenReturn(dummyView2);
+
+ when(mListView.getPositionForView(dummyView1)).thenReturn(POSITION - 1);
+ when(mListView.getPositionForView(mView)).thenReturn(POSITION);
+ when(mListView.getPositionForView(dummyView2)).thenReturn(POSITION + 1);
+
+ when(mListView.getHeaderViewsCount()).thenReturn(2);
+
+ assertThat(AdapterViewUtil.getPositionForView(mListView, mView), is(POSITION - 2));
+ }
+
+ @SuppressWarnings("ProtectedInnerClass")
+ protected abstract static class AbsListViewImpl extends AbsListView {
+
+ protected AbsListViewImpl(final Context context) {
+ super(context);
+ }
+
+ protected AbsListViewImpl(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ protected AbsListViewImpl(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AnimatorUtilTest.java b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AnimatorUtilTest.java
new file mode 100644
index 00000000..1136c741
--- /dev/null
+++ b/lib-core/src/androidTest/java/com/nhaarman/listviewanimations/util/AnimatorUtilTest.java
@@ -0,0 +1,71 @@
+package com.nhaarman.listviewanimations.util;
+
+
+import com.nineoldandroids.animation.Animator;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.*;
+
+
+public class AnimatorUtilTest extends TestCase {
+
+ private List mAnimators = new ArrayList<>();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ for (int i = 0; i < 5; i++) {
+ mAnimators.add(mock(Animator.class));
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mAnimators.clear();
+ }
+
+ public void testEmptyArrays() {
+ Animator[] animators = AnimatorUtil.concatAnimators(new Animator[0], new Animator[0], mAnimators.get(0));
+
+ assertThat(animators.length, is(1));
+ assertThat(animators[0], is(mAnimators.get(0)));
+ }
+
+ public void testSingleLengthArrays() {
+ Animator[] animators = AnimatorUtil.concatAnimators(
+ new Animator[]{mAnimators.get(0)},
+ new Animator[]{mAnimators.get(1)},
+ mAnimators.get(2)
+ );
+
+ assertThat(animators.length, is(3));
+ assertThat(animators[0], is(mAnimators.get(0)));
+ assertThat(animators[1], is(mAnimators.get(1)));
+ assertThat(animators[2], is(mAnimators.get(2)));
+ }
+
+ public void testMultipleLengthArrays() {
+ Animator[] animators = AnimatorUtil.concatAnimators(
+ new Animator[]{mAnimators.get(0), mAnimators.get(1)},
+ new Animator[]{mAnimators.get(2), mAnimators.get(3)},
+ mAnimators.get(4)
+ );
+
+ assertThat(animators.length, is(5));
+ assertThat(mAnimators.indexOf(animators[0]), is(0));
+ assertThat(mAnimators.indexOf(animators[1]), is(1));
+ assertThat(mAnimators.indexOf(animators[2]), is(2));
+ assertThat(mAnimators.indexOf(animators[3]), is(3));
+ assertThat(mAnimators.indexOf(animators[4]), is(4));
+ }
+}
\ No newline at end of file
diff --git a/lib-core/src/main/AndroidManifest.xml b/lib-core/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..e9894fb8
--- /dev/null
+++ b/lib-core/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/ArrayAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/ArrayAdapter.java
new file mode 100644
index 00000000..56cb9dd3
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/ArrayAdapter.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.util.Insertable;
+import com.nhaarman.listviewanimations.util.Swappable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A true {@link ArrayList} adapter providing access to some of the {@code ArrayList} methods.
+ *
+ * Also implements {@link Swappable} for easy object swapping,
+ * and {@link com.nhaarman.listviewanimations.util.Insertable} for inserting objects.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public abstract class ArrayAdapter extends BaseAdapter implements Swappable, Insertable {
+
+ @NonNull
+ private final List mItems;
+
+ private BaseAdapter mDataSetChangedSlavedAdapter;
+
+ /**
+ * Creates a new ArrayAdapter with an empty {@code List}.
+ */
+ protected ArrayAdapter() {
+ this(null);
+ }
+
+ /**
+ * Creates a new ArrayAdapter, using (a copy of) given {@code List}, or an empty {@code List} if objects = null.
+ */
+ protected ArrayAdapter(@Nullable final List objects) {
+ if (objects != null) {
+ mItems = objects;
+ } else {
+ mItems = new ArrayList<>();
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return position;
+ }
+
+ @Override
+ @NonNull
+ public T getItem(final int position) {
+ return mItems.get(position);
+ }
+
+ /**
+ * Returns the items.
+ */
+ @NonNull
+ public List getItems() {
+ return mItems;
+ }
+
+ /**
+ * Appends the specified element to the end of the {@code List}.
+ *
+ * @param object the object to add.
+ *
+ * @return always true.
+ */
+ public boolean add(@NonNull final T object) {
+ boolean result = mItems.add(object);
+ notifyDataSetChanged();
+ return result;
+ }
+
+ @Override
+ public void add(final int index, @NonNull final T item) {
+ mItems.add(index, item);
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Adds the objects in the specified collection to the end of this List. The objects are added in the order in which they are returned from the collection's iterator.
+ *
+ * @param collection the collection of objects.
+ *
+ * @return {@code true} if this {@code List} is modified, {@code false} otherwise.
+ */
+ public boolean addAll(@NonNull final Collection extends T> collection) {
+ boolean result = mItems.addAll(collection);
+ notifyDataSetChanged();
+ return result;
+ }
+
+ public boolean contains(final T object) {
+ return mItems.contains(object);
+ }
+
+ public void clear() {
+ mItems.clear();
+ notifyDataSetChanged();
+ }
+
+ public boolean remove(@NonNull final Object object) {
+ boolean result = mItems.remove(object);
+ notifyDataSetChanged();
+ return result;
+ }
+
+ @NonNull
+ public T remove(final int location) {
+ T result = mItems.remove(location);
+ notifyDataSetChanged();
+ return result;
+ }
+
+ @Override
+ public void swapItems(final int positionOne, final int positionTwo) {
+ T firstItem = mItems.set(positionOne, getItem(positionTwo));
+ notifyDataSetChanged();
+ mItems.set(positionTwo, firstItem);
+ }
+
+ public void propagateNotifyDataSetChanged(@NonNull final BaseAdapter slavedAdapter) {
+ mDataSetChangedSlavedAdapter = slavedAdapter;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ if (mDataSetChangedSlavedAdapter != null) {
+ mDataSetChangedSlavedAdapter.notifyDataSetChanged();
+ }
+ }
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/BaseAdapterDecorator.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/BaseAdapterDecorator.java
new file mode 100644
index 00000000..5f8ae49a
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/BaseAdapterDecorator.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations;
+
+import android.database.DataSetObserver;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.SectionIndexer;
+
+import com.nhaarman.listviewanimations.util.AbsListViewWrapper;
+import com.nhaarman.listviewanimations.util.Insertable;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nhaarman.listviewanimations.util.ListViewWrapperSetter;
+import com.nhaarman.listviewanimations.util.Swappable;
+
+/**
+ * A decorator class that enables decoration of an instance of the {@link BaseAdapter} class.
+ *
+ * Classes extending this class can override methods and provide extra functionality before or after calling the super method.
+ */
+public abstract class BaseAdapterDecorator extends BaseAdapter implements SectionIndexer, Swappable, Insertable, ListViewWrapperSetter {
+
+ /**
+ * The {@link android.widget.BaseAdapter} this {@code BaseAdapterDecorator} decorates.
+ */
+ @NonNull
+ private final BaseAdapter mDecoratedBaseAdapter;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.util.ListViewWrapper} containing the ListView this {@code BaseAdapterDecorator} will be bound to.
+ */
+ @Nullable
+ private ListViewWrapper mListViewWrapper;
+
+ /**
+ * Create a new {@code BaseAdapterDecorator}, decorating given {@link android.widget.BaseAdapter}.
+ *
+ * @param baseAdapter the {@code} BaseAdapter to decorate.
+ */
+ protected BaseAdapterDecorator(@NonNull final BaseAdapter baseAdapter) {
+ mDecoratedBaseAdapter = baseAdapter;
+ }
+
+ /**
+ * Returns the {@link android.widget.BaseAdapter} that this {@code BaseAdapterDecorator} decorates.
+ */
+ @NonNull
+ public BaseAdapter getDecoratedBaseAdapter() {
+ return mDecoratedBaseAdapter;
+ }
+
+ /**
+ * Returns the root {@link android.widget.BaseAdapter} this {@code BaseAdapterDecorator} decorates.
+ */
+ @NonNull
+ protected BaseAdapter getRootAdapter() {
+ BaseAdapter adapter = mDecoratedBaseAdapter;
+ while (adapter instanceof BaseAdapterDecorator) {
+ adapter = ((BaseAdapterDecorator) adapter).getDecoratedBaseAdapter();
+ }
+ return adapter;
+ }
+
+ public void setAbsListView(@NonNull final AbsListView absListView) {
+ setListViewWrapper(new AbsListViewWrapper(absListView));
+ }
+
+ /**
+ * Returns the {@link com.nhaarman.listviewanimations.util.ListViewWrapper} containing the ListView this {@code BaseAdapterDecorator} is bound to.
+ */
+ @Nullable
+ public ListViewWrapper getListViewWrapper() {
+ return mListViewWrapper;
+ }
+
+ /**
+ * Alternative to {@link #setAbsListView(android.widget.AbsListView)}. Sets the {@link com.nhaarman.listviewanimations.util.ListViewWrapper} which contains the ListView
+ * this adapter will be bound to. Call this method before setting this adapter to the ListView. Also propagates to the decorated {@code BaseAdapter} if applicable.
+ */
+ @Override
+ public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) {
+ mListViewWrapper = listViewWrapper;
+
+ if (mDecoratedBaseAdapter instanceof ListViewWrapperSetter) {
+ ((ListViewWrapperSetter) mDecoratedBaseAdapter).setListViewWrapper(listViewWrapper);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return mDecoratedBaseAdapter.getCount();
+ }
+
+ @Override
+ public Object getItem(final int position) {
+ return mDecoratedBaseAdapter.getItem(position);
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return mDecoratedBaseAdapter.getItemId(position);
+ }
+
+ @Override
+ @NonNull
+ public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ return mDecoratedBaseAdapter.getView(position, convertView, parent);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mDecoratedBaseAdapter.areAllItemsEnabled();
+ }
+
+ @Override
+ @NonNull
+ public View getDropDownView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ return mDecoratedBaseAdapter.getDropDownView(position, convertView, parent);
+ }
+
+ @Override
+ public int getItemViewType(final int position) {
+ return mDecoratedBaseAdapter.getItemViewType(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return mDecoratedBaseAdapter.getViewTypeCount();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return mDecoratedBaseAdapter.hasStableIds();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mDecoratedBaseAdapter.isEmpty();
+ }
+
+ @Override
+ public boolean isEnabled(final int position) {
+ return mDecoratedBaseAdapter.isEnabled(position);
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ if (!(mDecoratedBaseAdapter instanceof ArrayAdapter>)) {
+ // fix #35 dirty trick !
+ // leads to an infinite loop when trying because ArrayAdapter triggers notifyDataSetChanged itself
+ // TODO: investigate
+ mDecoratedBaseAdapter.notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Helper function if you want to force notifyDataSetChanged()
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public void notifyDataSetChanged(final boolean force) {
+ if (force || !(mDecoratedBaseAdapter instanceof ArrayAdapter>)) {
+ // leads to an infinite loop when trying because ArrayAdapter triggers notifyDataSetChanged itself
+ // TODO: investigate
+ mDecoratedBaseAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ mDecoratedBaseAdapter.notifyDataSetInvalidated();
+ }
+
+ @Override
+ public void registerDataSetObserver(@NonNull final DataSetObserver observer) {
+ mDecoratedBaseAdapter.registerDataSetObserver(observer);
+ }
+
+ @Override
+ public void unregisterDataSetObserver(@NonNull final DataSetObserver observer) {
+ mDecoratedBaseAdapter.unregisterDataSetObserver(observer);
+ }
+
+ @Override
+ public int getPositionForSection(final int sectionIndex) {
+ int result = 0;
+ if (mDecoratedBaseAdapter instanceof SectionIndexer) {
+ result = ((SectionIndexer) mDecoratedBaseAdapter).getPositionForSection(sectionIndex);
+ }
+ return result;
+ }
+
+ @Override
+ public int getSectionForPosition(final int position) {
+ int result = 0;
+ if (mDecoratedBaseAdapter instanceof SectionIndexer) {
+ result = ((SectionIndexer) mDecoratedBaseAdapter).getSectionForPosition(position);
+ }
+ return result;
+ }
+
+ @Override
+ @NonNull
+ public Object[] getSections() {
+ Object[] result = new Object[0];
+ if (mDecoratedBaseAdapter instanceof SectionIndexer) {
+ result = ((SectionIndexer) mDecoratedBaseAdapter).getSections();
+ }
+ return result;
+ }
+
+ @Override
+ public void swapItems(final int positionOne, final int positionTwo) {
+ if (mDecoratedBaseAdapter instanceof Swappable) {
+ ((Swappable) mDecoratedBaseAdapter).swapItems(positionOne, positionTwo);
+ } else {
+ Log.w("ListViewAnimations", "Warning: swapItems called on an adapter that does not implement Swappable!");
+ }
+ }
+
+ @Override
+ public void add(final int index, @NonNull final Object item) {
+ if (mDecoratedBaseAdapter instanceof Insertable) {
+ //noinspection rawtypes
+ ((Insertable) mDecoratedBaseAdapter).add(index, item);
+ } else {
+ Log.w("ListViewAnimations", "Warning: add called on an adapter that does not implement Insertable!");
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/AnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/AnimationAdapter.java
new file mode 100644
index 00000000..26641d26
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/AnimationAdapter.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.util.AnimatorUtil;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+/**
+ * A {@link BaseAdapterDecorator} class which applies multiple {@link Animator}s at once to views when they are first shown. The Animators applied include the animations specified
+ * in {@link #getAnimators(ViewGroup, View)}, plus an alpha transition.
+ */
+public abstract class AnimationAdapter extends BaseAdapterDecorator {
+
+ /**
+ * Saved instance state key for the ViewAniamt
+ */
+ private static final String SAVEDINSTANCESTATE_VIEWANIMATOR = "savedinstancestate_viewanimator";
+
+ /**
+ * Alpha property
+ */
+ private static final String ALPHA = "alpha";
+
+ /**
+ * The ViewAnimator responsible for animating the Views.
+ */
+ @Nullable
+ private ViewAnimator mViewAnimator;
+
+ /**
+ * Whether this instance is the root AnimationAdapter. When this is set to false, animation is not applied to the views, since the wrapper AnimationAdapter will take care of
+ * that.
+ */
+ private boolean mIsRootAdapter;
+
+ /**
+ * If the AbsListView is an instance of GridView, this boolean indicates whether the GridView is possibly measuring the view.
+ */
+ private boolean mGridViewPossiblyMeasuring;
+
+ /**
+ * The position of the item that the GridView is possibly measuring.
+ */
+ private int mGridViewMeasuringPosition;
+
+ /**
+ * Creates a new AnimationAdapter, wrapping given BaseAdapter.
+ *
+ * @param baseAdapter the BaseAdapter to wrap.
+ */
+ protected AnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
+ super(baseAdapter);
+
+ mGridViewPossiblyMeasuring = true;
+ mGridViewMeasuringPosition = -1;
+ mIsRootAdapter = true;
+
+ if (baseAdapter instanceof AnimationAdapter) {
+ ((AnimationAdapter) baseAdapter).setIsWrapped();
+ }
+ }
+
+ @Override
+ public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) {
+ super.setListViewWrapper(listViewWrapper);
+ mViewAnimator = new ViewAnimator(listViewWrapper);
+ }
+
+ /**
+ * Sets whether this instance is wrapped by another instance of AnimationAdapter. If called, this instance will not apply any animations to the views, since the wrapper
+ * AnimationAdapter handles that.
+ */
+ private void setIsWrapped() {
+ mIsRootAdapter = false;
+ }
+
+ /**
+ * Call this method to reset animation status on all views. The next time {@link #notifyDataSetChanged()} is called on the base adapter, all views will animate again.
+ */
+ public void reset() {
+ if (getListViewWrapper() == null) {
+ throw new IllegalStateException("Call setAbsListView() on this AnimationAdapter first!");
+ }
+
+ assert mViewAnimator != null;
+ mViewAnimator.reset();
+
+ mGridViewPossiblyMeasuring = true;
+ mGridViewMeasuringPosition = -1;
+
+ if (getDecoratedBaseAdapter() instanceof AnimationAdapter) {
+ ((AnimationAdapter) getDecoratedBaseAdapter()).reset();
+ }
+ }
+
+ /**
+ * Returns the {@link com.nhaarman.listviewanimations.appearance.ViewAnimator} responsible for animating the Views in this adapter.
+ */
+ @Nullable
+ public ViewAnimator getViewAnimator() {
+ return mViewAnimator;
+ }
+
+ @NonNull
+ @Override
+ public final View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ if (mIsRootAdapter) {
+ if (getListViewWrapper() == null) {
+ throw new IllegalStateException("Call setAbsListView() on this AnimationAdapter first!");
+ }
+
+ assert mViewAnimator != null;
+ if (convertView != null) {
+ mViewAnimator.cancelExistingAnimation(convertView);
+ }
+ }
+
+ View itemView = super.getView(position, convertView, parent);
+
+ if (mIsRootAdapter) {
+ animateViewIfNecessary(position, itemView, parent);
+ }
+ return itemView;
+ }
+
+ /**
+ * Animates given View if necessary.
+ *
+ * @param position the position of the item the View represents.
+ * @param view the View that should be animated.
+ * @param parent the parent the View is hosted in.
+ */
+ private void animateViewIfNecessary(final int position, @NonNull final View view, @NonNull final ViewGroup parent) {
+ assert mViewAnimator != null;
+
+ /* GridView measures the first View which is returned by getView(int, View, ViewGroup), but does not use that View.
+ On KitKat, it does this actually multiple times.
+ Therefore, we animate all these first Views, and reset the last animated position when we suspect GridView is measuring. */
+ mGridViewPossiblyMeasuring = mGridViewPossiblyMeasuring && (mGridViewMeasuringPosition == -1 || mGridViewMeasuringPosition == position);
+
+ if (mGridViewPossiblyMeasuring) {
+ mGridViewMeasuringPosition = position;
+ mViewAnimator.setLastAnimatedPosition(-1);
+ }
+
+ Animator[] childAnimators;
+ if (getDecoratedBaseAdapter() instanceof AnimationAdapter) {
+ childAnimators = ((AnimationAdapter) getDecoratedBaseAdapter()).getAnimators(parent, view);
+ } else {
+ childAnimators = new Animator[0];
+ }
+ Animator[] animators = getAnimators(parent, view);
+ Animator alphaAnimator = ObjectAnimator.ofFloat(view, ALPHA, 0, 1);
+
+ Animator[] concatAnimators = AnimatorUtil.concatAnimators(childAnimators, animators, alphaAnimator);
+ mViewAnimator.animateViewIfNecessary(position, view, concatAnimators);
+ }
+
+ /**
+ * Returns the Animators to apply to the views. In addition to the returned Animators, an alpha transition will be applied to the view.
+ *
+ * @param parent The parent of the view
+ * @param view The view that will be animated, as retrieved by getView().
+ */
+ @NonNull
+ public abstract Animator[] getAnimators(@NonNull ViewGroup parent, @NonNull View view);
+
+ /**
+ * Returns a Parcelable object containing the AnimationAdapter's current dynamic state.
+ */
+ @NonNull
+ public Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+
+ if (mViewAnimator != null) {
+ bundle.putParcelable(SAVEDINSTANCESTATE_VIEWANIMATOR, mViewAnimator.onSaveInstanceState());
+ }
+
+ return bundle;
+ }
+
+ /**
+ * Restores this AnimationAdapter's state.
+ *
+ * @param parcelable the Parcelable object previously returned by {@link #onSaveInstanceState()}.
+ */
+ public void onRestoreInstanceState(@Nullable final Parcelable parcelable) {
+ if (parcelable instanceof Bundle) {
+ Bundle bundle = (Bundle) parcelable;
+ if (mViewAnimator != null) {
+ mViewAnimator.onRestoreInstanceState(bundle.getParcelable(SAVEDINSTANCESTATE_VIEWANIMATOR));
+ }
+ }
+ }
+}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/ResourceAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ResourceAnimationAdapter.java
similarity index 74%
rename from library/src/com/nhaarman/listviewanimations/swinginadapters/ResourceAnimationAdapter.java
rename to lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ResourceAnimationAdapter.java
index 17d39031..7573d2c2 100644
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/ResourceAnimationAdapter.java
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ResourceAnimationAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Niek Haarman
+ * Copyright 2014 Niek Haarman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.nhaarman.listviewanimations.swinginadapters;
+package com.nhaarman.listviewanimations.appearance;
import android.content.Context;
import android.view.View;
@@ -23,22 +23,26 @@
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorInflater;
+import android.support.annotation.NonNull;
+
/**
* An implementation of AnimationAdapter which bases the animations on
* resources.
*/
-public abstract class ResourceAnimationAdapter extends AnimationAdapter {
+public abstract class ResourceAnimationAdapter extends AnimationAdapter {
+ @NonNull
private final Context mContext;
@SuppressWarnings("UnusedDeclaration")
- public ResourceAnimationAdapter(final BaseAdapter baseAdapter, final Context context) {
+ protected ResourceAnimationAdapter(@NonNull final BaseAdapter baseAdapter, @NonNull final Context context) {
super(baseAdapter);
mContext = context;
}
+ @NonNull
@Override
- public Animator[] getAnimators(final ViewGroup parent, final View view) {
+ public Animator[] getAnimators(@NonNull final ViewGroup parent, @NonNull final View view) {
return new Animator[]{AnimatorInflater.loadAnimator(mContext, getAnimationResourceId())};
}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/SingleAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/SingleAnimationAdapter.java
similarity index 63%
rename from library/src/com/nhaarman/listviewanimations/swinginadapters/SingleAnimationAdapter.java
rename to lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/SingleAnimationAdapter.java
index 7d744d40..1b93e5ed 100644
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/SingleAnimationAdapter.java
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/SingleAnimationAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Niek Haarman
+ * Copyright 2014 Niek Haarman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.nhaarman.listviewanimations.swinginadapters;
+package com.nhaarman.listviewanimations.appearance;
import android.view.View;
import android.view.ViewGroup;
@@ -21,18 +21,21 @@
import com.nineoldandroids.animation.Animator;
+import android.support.annotation.NonNull;
+
/**
* An implementation of AnimationAdapter which applies a single Animator to
* views.
*/
public abstract class SingleAnimationAdapter extends AnimationAdapter {
- public SingleAnimationAdapter(final BaseAdapter baseAdapter) {
+ protected SingleAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
super(baseAdapter);
}
+ @NonNull
@Override
- public Animator[] getAnimators(final ViewGroup parent, final View view) {
+ public Animator[] getAnimators(@NonNull final ViewGroup parent, @NonNull final View view) {
Animator animator = getAnimator(parent, view);
return new Animator[]{animator};
}
@@ -40,12 +43,11 @@ public Animator[] getAnimators(final ViewGroup parent, final View view) {
/**
* Get the {@link Animator} to apply to the {@link View}.
*
- * @param parent
- * the {@link ViewGroup} which is the parent of the View.
- * @param view
- * the View that will be animated, as retrieved by
- * {@link #getView(int, View, ViewGroup)}.
+ * @param parent the {@link ViewGroup} which is the parent of the View.
+ * @param view the View that will be animated, as retrieved by
+ * {@link #getView(int, View, ViewGroup)}.
*/
- protected abstract Animator getAnimator(ViewGroup parent, View view);
+ @NonNull
+ protected abstract Animator getAnimator(@NonNull ViewGroup parent, @NonNull View view);
}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ViewAnimator.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ViewAnimator.java
new file mode 100644
index 00000000..6bc9af48
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/ViewAnimator.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.SparseArray;
+import android.view.View;
+import android.widget.GridView;
+
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorSet;
+import com.nineoldandroids.view.ViewHelper;
+
+/**
+ * A class which decides whether given Views should be animated based on their position: each View should only be animated once.
+ * It also calculates proper animation delays for the views.
+ */
+public class ViewAnimator {
+
+ /* Saved instance state keys */
+ private static final String SAVEDINSTANCESTATE_FIRSTANIMATEDPOSITION = "savedinstancestate_firstanimatedposition";
+ private static final String SAVEDINSTANCESTATE_LASTANIMATEDPOSITION = "savedinstancestate_lastanimatedposition";
+ private static final String SAVEDINSTANCESTATE_SHOULDANIMATE = "savedinstancestate_shouldanimate";
+
+ /* Default values */
+
+ /**
+ * The default delay in millis before the first animation starts.
+ */
+ private static final int INITIAL_DELAY_MILLIS = 150;
+
+ /**
+ * The default delay in millis between view animations.
+ */
+ private static final int DEFAULT_ANIMATION_DELAY_MILLIS = 100;
+
+ /**
+ * The default duration in millis of the animations.
+ */
+ private static final int DEFAULT_ANIMATION_DURATION_MILLIS = 300;
+
+ /* Fields */
+
+ /**
+ * The ListViewWrapper containing the ListView implementation.
+ */
+ @NonNull
+ private final ListViewWrapper mListViewWrapper;
+
+ /**
+ * The active Animators. Keys are hashcodes of the Views that are animated.
+ */
+ @NonNull
+ private final SparseArray mAnimators = new SparseArray<>();
+
+ /**
+ * The delay in millis before the first animation starts.
+ */
+ private int mInitialDelayMillis = INITIAL_DELAY_MILLIS;
+
+ /**
+ * The delay in millis between view animations.
+ */
+ private int mAnimationDelayMillis = DEFAULT_ANIMATION_DELAY_MILLIS;
+
+ /**
+ * The duration in millis of the animations.
+ */
+ private int mAnimationDurationMillis = DEFAULT_ANIMATION_DURATION_MILLIS;
+
+ /**
+ * The start timestamp of the first animation, as returned by {@link android.os.SystemClock#uptimeMillis()}.
+ */
+ private long mAnimationStartMillis;
+
+ /**
+ * The position of the item that is the first that was animated.
+ */
+ private int mFirstAnimatedPosition;
+
+ /**
+ * The position of the last item that was animated.
+ */
+ private int mLastAnimatedPosition;
+
+ /**
+ * Whether animation is enabled. When this is set to false, no animation is applied to the views.
+ */
+ private boolean mShouldAnimate = true;
+
+ /**
+ * Creates a new ViewAnimator, using the given {@link com.nhaarman.listviewanimations.util.ListViewWrapper}.
+ *
+ * @param listViewWrapper the {@code ListViewWrapper} which wraps the implementation of the ListView used.
+ */
+ public ViewAnimator(@NonNull final ListViewWrapper listViewWrapper) {
+ mListViewWrapper = listViewWrapper;
+ mAnimationStartMillis = -1;
+ mFirstAnimatedPosition = -1;
+ mLastAnimatedPosition = -1;
+ }
+
+ /**
+ * Call this method to reset animation status on all views.
+ */
+ public void reset() {
+ for (int i = 0; i < mAnimators.size(); i++) {
+ mAnimators.get(mAnimators.keyAt(i)).cancel();
+ }
+ mAnimators.clear();
+ mFirstAnimatedPosition = -1;
+ mLastAnimatedPosition = -1;
+ mAnimationStartMillis = -1;
+ mShouldAnimate = true;
+ }
+
+ /**
+ * Set the starting position for which items should animate. Given position will animate as well.
+ * Will also call {@link #enableAnimations()}.
+ *
+ * @param position the position.
+ */
+ public void setShouldAnimateFromPosition(final int position) {
+ enableAnimations();
+ mFirstAnimatedPosition = position - 1;
+ mLastAnimatedPosition = position - 1;
+ }
+
+ /**
+ * Set the starting position for which items should animate as the first position which isn't currently visible on screen. This call is also valid when the {@link View}s
+ * haven't been drawn yet. Will also call {@link #enableAnimations()}.
+ */
+ public void setShouldAnimateNotVisible() {
+ enableAnimations();
+ mFirstAnimatedPosition = mListViewWrapper.getLastVisiblePosition();
+ mLastAnimatedPosition = mListViewWrapper.getLastVisiblePosition();
+ }
+
+ /**
+ * Sets the value of the last animated position. Views with positions smaller than or equal to given value will not be animated.
+ */
+ void setLastAnimatedPosition(final int lastAnimatedPosition) {
+ mLastAnimatedPosition = lastAnimatedPosition;
+ }
+
+ /**
+ * Sets the delay in milliseconds before the first animation should start. Defaults to {@value #INITIAL_DELAY_MILLIS}.
+ *
+ * @param delayMillis the time in milliseconds.
+ */
+ public void setInitialDelayMillis(final int delayMillis) {
+ mInitialDelayMillis = delayMillis;
+ }
+
+ /**
+ * Sets the delay in milliseconds before an animation of a view should start. Defaults to {@value #DEFAULT_ANIMATION_DELAY_MILLIS}.
+ *
+ * @param delayMillis the time in milliseconds.
+ */
+ public void setAnimationDelayMillis(final int delayMillis) {
+ mAnimationDelayMillis = delayMillis;
+ }
+
+ /**
+ * Sets the duration of the animation in milliseconds. Defaults to {@value #DEFAULT_ANIMATION_DURATION_MILLIS}.
+ *
+ * @param durationMillis the time in milliseconds.
+ */
+ public void setAnimationDurationMillis(final int durationMillis) {
+ mAnimationDurationMillis = durationMillis;
+ }
+
+ /**
+ * Enables animating the Views. This is the default.
+ */
+ public void enableAnimations() {
+ mShouldAnimate = true;
+ }
+
+ /**
+ * Disables animating the Views. Enable them again using {@link #enableAnimations()}.
+ */
+ public void disableAnimations() {
+ mShouldAnimate = false;
+ }
+
+ /**
+ * Cancels any existing animations for given View.
+ */
+ void cancelExistingAnimation(@NonNull final View view) {
+ int hashCode = view.hashCode();
+ Animator animator = mAnimators.get(hashCode);
+ if (animator != null) {
+ animator.end();
+ mAnimators.remove(hashCode);
+ }
+ }
+
+ /**
+ * Animates given View if necessary.
+ *
+ * @param position the position of the item the View represents.
+ * @param view the View that should be animated.
+ */
+ public void animateViewIfNecessary(final int position, @NonNull final View view, @NonNull final Animator[] animators) {
+ if (mShouldAnimate && position > mLastAnimatedPosition) {
+ if (mFirstAnimatedPosition == -1) {
+ mFirstAnimatedPosition = position;
+ }
+
+ animateView(position, view, animators);
+ mLastAnimatedPosition = position;
+ }
+ }
+
+ /**
+ * Animates given View.
+ *
+ * @param view the View that should be animated.
+ */
+ private void animateView(final int position, @NonNull final View view, @NonNull final Animator[] animators) {
+ if (mAnimationStartMillis == -1) {
+ mAnimationStartMillis = SystemClock.uptimeMillis();
+ }
+
+ ViewHelper.setAlpha(view, 0);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animators);
+ set.setStartDelay(calculateAnimationDelay(position));
+ set.setDuration(mAnimationDurationMillis);
+ set.start();
+
+ mAnimators.put(view.hashCode(), set);
+ }
+
+ /**
+ * Returns the delay in milliseconds after which animation for View with position mLastAnimatedPosition + 1 should start.
+ */
+ @SuppressLint("NewApi")
+ private int calculateAnimationDelay(final int position) {
+ int delay;
+
+ int lastVisiblePosition = mListViewWrapper.getLastVisiblePosition();
+ int firstVisiblePosition = mListViewWrapper.getFirstVisiblePosition();
+
+ int numberOfItemsOnScreen = lastVisiblePosition - firstVisiblePosition;
+ int numberOfAnimatedItems = position - 1 - mFirstAnimatedPosition;
+
+ if (numberOfItemsOnScreen + 1 < numberOfAnimatedItems) {
+ delay = mAnimationDelayMillis;
+
+ if (mListViewWrapper.getListView() instanceof GridView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ int numColumns = ((GridView) mListViewWrapper.getListView()).getNumColumns();
+ delay += mAnimationDelayMillis * (position % numColumns);
+ }
+ } else {
+ int delaySinceStart = (position - mFirstAnimatedPosition) * mAnimationDelayMillis;
+ delay = Math.max(0, (int) (-SystemClock.uptimeMillis() + mAnimationStartMillis + mInitialDelayMillis + delaySinceStart));
+ }
+ return delay;
+ }
+
+ /**
+ * Returns a Parcelable object containing the AnimationAdapter's current dynamic state.
+ */
+ @NonNull
+ public Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+
+ bundle.putInt(SAVEDINSTANCESTATE_FIRSTANIMATEDPOSITION, mFirstAnimatedPosition);
+ bundle.putInt(SAVEDINSTANCESTATE_LASTANIMATEDPOSITION, mLastAnimatedPosition);
+ bundle.putBoolean(SAVEDINSTANCESTATE_SHOULDANIMATE, mShouldAnimate);
+
+ return bundle;
+ }
+
+ /**
+ * Restores this AnimationAdapter's state.
+ *
+ * @param parcelable the Parcelable object previously returned by {@link #onSaveInstanceState()}.
+ */
+ public void onRestoreInstanceState(@Nullable final Parcelable parcelable) {
+ if (parcelable instanceof Bundle) {
+ Bundle bundle = (Bundle) parcelable;
+ mFirstAnimatedPosition = bundle.getInt(SAVEDINSTANCESTATE_FIRSTANIMATEDPOSITION);
+ mLastAnimatedPosition = bundle.getInt(SAVEDINSTANCESTATE_LASTANIMATEDPOSITION);
+ mShouldAnimate = bundle.getBoolean(SAVEDINSTANCESTATE_SHOULDANIMATE);
+ }
+ }
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/AlphaInAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/AlphaInAnimationAdapter.java
new file mode 100644
index 00000000..e07eba8a
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/AlphaInAnimationAdapter.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance.simple;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.appearance.AnimationAdapter;
+import com.nineoldandroids.animation.Animator;
+
+public class AlphaInAnimationAdapter extends AnimationAdapter {
+
+ public AlphaInAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
+ super(baseAdapter);
+ }
+
+ @NonNull
+ @Override
+ public Animator[] getAnimators(@NonNull final ViewGroup parent, @NonNull final View view) {
+ return new Animator[0];
+ }
+}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/ScaleInAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/ScaleInAnimationAdapter.java
similarity index 51%
rename from library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/ScaleInAnimationAdapter.java
rename to lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/ScaleInAnimationAdapter.java
index 90f075a4..ba92486b 100644
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/ScaleInAnimationAdapter.java
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/ScaleInAnimationAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Niek Haarman
+ * Copyright 2014 Niek Haarman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,53 +13,38 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.nhaarman.listviewanimations.swinginadapters.prepared;
+package com.nhaarman.listviewanimations.appearance.simple;
+import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.AnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.AnimationAdapter;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
public class ScaleInAnimationAdapter extends AnimationAdapter {
- private static final float DEFAULTSCALEFROM = 0.8f;
+ private static final float DEFAULT_SCALE_FROM = 0.8f;
+
private static final String SCALE_X = "scaleX";
private static final String SCALE_Y = "scaleY";
private final float mScaleFrom;
- private final long mAnimationDelayMillis;
- private final long mAnimationDurationMillis;
-
- public ScaleInAnimationAdapter(final BaseAdapter baseAdapter) {
- this(baseAdapter, DEFAULTSCALEFROM);
- }
- public ScaleInAnimationAdapter(final BaseAdapter baseAdapter, final float scaleFrom) {
- this(baseAdapter, scaleFrom, DEFAULTANIMATIONDELAYMILLIS, DEFAULTANIMATIONDURATIONMILLIS);
+ public ScaleInAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
+ this(baseAdapter, DEFAULT_SCALE_FROM);
}
- public ScaleInAnimationAdapter(final BaseAdapter baseAdapter, final float scaleFrom, final long animationDelayMillis, final long animationDurationMillis) {
+ public ScaleInAnimationAdapter(@NonNull final BaseAdapter baseAdapter, final float scaleFrom) {
super(baseAdapter);
mScaleFrom = scaleFrom;
- mAnimationDelayMillis = animationDelayMillis;
- mAnimationDurationMillis = animationDurationMillis;
- }
-
- @Override
- protected long getAnimationDelayMillis() {
- return mAnimationDelayMillis;
- }
-
- @Override
- protected long getAnimationDurationMillis() {
- return mAnimationDurationMillis;
}
+ @NonNull
@Override
- public Animator[] getAnimators(final ViewGroup parent, final View view) {
+ public Animator[] getAnimators(@NonNull final ViewGroup parent, @NonNull final View view) {
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, SCALE_X, mScaleFrom, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, SCALE_Y, mScaleFrom, 1f);
return new ObjectAnimator[]{scaleX, scaleY};
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingBottomInAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingBottomInAnimationAdapter.java
new file mode 100644
index 00000000..37717e2f
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingBottomInAnimationAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance.simple;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.appearance.SingleAnimationAdapter;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+/**
+ * An implementation of the AnimationAdapter class which applies a
+ * swing-in-from-bottom-animation to views.
+ */
+public class SwingBottomInAnimationAdapter extends SingleAnimationAdapter {
+
+ private static final String TRANSLATION_Y = "translationY";
+
+ public SwingBottomInAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
+ super(baseAdapter);
+ }
+
+ @Override
+ @NonNull
+ protected Animator getAnimator(@NonNull final ViewGroup parent, @NonNull final View view) {
+ return ObjectAnimator.ofFloat(view, TRANSLATION_Y, parent.getMeasuredHeight() >> 1, 0);
+ }
+
+}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingLeftInAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingLeftInAnimationAdapter.java
similarity index 50%
rename from library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingLeftInAnimationAdapter.java
rename to lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingLeftInAnimationAdapter.java
index cc629446..035a383e 100644
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingLeftInAnimationAdapter.java
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingLeftInAnimationAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 Niek Haarman
+ * Copyright 2014 Niek Haarman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.nhaarman.listviewanimations.swinginadapters.prepared;
+package com.nhaarman.listviewanimations.appearance.simple;
+import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import com.nhaarman.listviewanimations.swinginadapters.SingleAnimationAdapter;
+import com.nhaarman.listviewanimations.appearance.SingleAnimationAdapter;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
@@ -31,35 +32,14 @@
public class SwingLeftInAnimationAdapter extends SingleAnimationAdapter {
private static final String TRANSLATION_X = "translationX";
- private final long mAnimationDelayMillis;
- private final long mAnimationDurationMillis;
- public SwingLeftInAnimationAdapter(final BaseAdapter baseAdapter) {
- this(baseAdapter, DEFAULTANIMATIONDELAYMILLIS, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingLeftInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis) {
- this(baseAdapter, animationDelayMillis, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingLeftInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis, final long animationDurationMillis) {
+ public SwingLeftInAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
super(baseAdapter);
- mAnimationDelayMillis = animationDelayMillis;
- mAnimationDurationMillis = animationDurationMillis;
- }
-
- @Override
- protected long getAnimationDelayMillis() {
- return mAnimationDelayMillis;
- }
-
- @Override
- protected long getAnimationDurationMillis() {
- return mAnimationDurationMillis;
}
+ @NonNull
@Override
- protected Animator getAnimator(final ViewGroup parent, final View view) {
+ protected Animator getAnimator(@NonNull final ViewGroup parent, @NonNull final View view) {
return ObjectAnimator.ofFloat(view, TRANSLATION_X, 0 - parent.getWidth(), 0);
}
}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingRightInAnimationAdapter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingRightInAnimationAdapter.java
new file mode 100644
index 00000000..ca8fbd5d
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/appearance/simple/SwingRightInAnimationAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.appearance.simple;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.appearance.SingleAnimationAdapter;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+/**
+ * An implementation of the AnimationAdapter class which applies a
+ * swing-in-from-the-right-animation to views.
+ */
+public class SwingRightInAnimationAdapter extends SingleAnimationAdapter {
+
+ private static final String TRANSLATION_X = "translationX";
+
+ public SwingRightInAnimationAdapter(@NonNull final BaseAdapter baseAdapter) {
+ super(baseAdapter);
+ }
+
+ @NonNull
+ @Override
+ protected Animator getAnimator(@NonNull final ViewGroup parent, @NonNull final View view) {
+ return ObjectAnimator.ofFloat(view, TRANSLATION_X, parent.getWidth(), 0);
+ }
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AbsListViewWrapper.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AbsListViewWrapper.java
new file mode 100644
index 00000000..1c0cc614
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AbsListViewWrapper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+public class AbsListViewWrapper implements ListViewWrapper {
+
+ @NonNull
+ private final AbsListView mAbsListView;
+
+ public AbsListViewWrapper(@NonNull final AbsListView absListView) {
+ mAbsListView = absListView;
+ }
+
+ @Override
+ @NonNull
+ public AbsListView getListView() {
+ return mAbsListView;
+ }
+
+ @Nullable
+ @Override
+ public View getChildAt(final int index) {
+ return mAbsListView.getChildAt(index);
+ }
+
+ @Override
+ public int getFirstVisiblePosition() {
+ return mAbsListView.getFirstVisiblePosition();
+ }
+
+ @Override
+ public int getLastVisiblePosition() {
+ return mAbsListView.getLastVisiblePosition();
+ }
+
+ @Override
+ public int getCount() {
+ return mAbsListView.getCount();
+ }
+
+ @Override
+ public int getChildCount() {
+ return mAbsListView.getChildCount();
+ }
+
+ @Override
+ public int getHeaderViewsCount() {
+ int result = 0;
+ if (mAbsListView instanceof ListView) {
+ result = ((ListView) mAbsListView).getHeaderViewsCount();
+ }
+ return result;
+ }
+
+ @Override
+ public int getPositionForView(@NonNull final View view) {
+ return mAbsListView.getPositionForView(view);
+ }
+
+ @Override
+ public ListAdapter getAdapter() {
+ return mAbsListView.getAdapter();
+ }
+
+ @Override
+ public void smoothScrollBy(final int distance, final int duration) {
+ mAbsListView.smoothScrollBy(distance, duration);
+ }
+
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AdapterViewUtil.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AdapterViewUtil.java
new file mode 100644
index 00000000..2c6b19fc
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AdapterViewUtil.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.ListView;
+
+public class AdapterViewUtil {
+
+ private AdapterViewUtil() {
+ }
+
+ /**
+ * Returns the position within the adapter's dataset for the view, where view is an adapter item or a descendant of an adapter item.
+ * Unlike {@link AdapterView#getPositionForView(android.view.View)}, returned position will reflect the position of the item given view is representing,
+ * by subtracting the header views count.
+ *
+ * @param listViewWrapper the IListViewWrapper wrapping the ListView containing the view.
+ * @param view an adapter item or a descendant of an adapter item. This must be visible in given AdapterView at the time of the call.
+ *
+ * @return the position of the item in the AdapterView represented by given view, or {@link AdapterView#INVALID_POSITION} if the view does not
+ * correspond to a list item (or it is not visible).
+ */
+ public static int getPositionForView(@NonNull final ListViewWrapper listViewWrapper, @NonNull final View view) {
+ return listViewWrapper.getPositionForView(view) - listViewWrapper.getHeaderViewsCount();
+ }
+
+ /**
+ * Returns the position within the adapter's dataset for the view, where view is an adapter item or a descendant of an adapter item.
+ * Unlike {@link AdapterView#getPositionForView(android.view.View)}, returned position will reflect the position of the item given view is representing,
+ * by subtracting the header views count.
+ *
+ * @param absListView the ListView containing the view.
+ * @param view an adapter item or a descendant of an adapter item. This must be visible in given AdapterView at the time of the call.
+ *
+ * @return the position of the item in the AdapterView represented by given view, or {@link AdapterView#INVALID_POSITION} if the view does not
+ * correspond to a list item (or it is not visible).
+ */
+ public static int getPositionForView(@NonNull final AbsListView absListView, @NonNull final View view) {
+ int position = absListView.getPositionForView(view);
+ if (absListView instanceof ListView) {
+ position -= ((ListView) absListView).getHeaderViewsCount();
+ }
+ return position;
+ }
+
+ /**
+ * Returns the {@link View} that represents the item for given position.
+ *
+ * @param listViewWrapper the {@link ListViewWrapper} wrapping the ListView that should be examined
+ * @param position the position for which the {@code View} should be returned.
+ *
+ * @return the {@code View}, or {@code null} if the position is not currently visible.
+ */
+ @Nullable
+ public static View getViewForPosition(@NonNull final ListViewWrapper listViewWrapper, final int position) {
+ int childCount = listViewWrapper.getChildCount();
+ View downView = null;
+ for (int i = 0; i < childCount && downView == null; i++) {
+ View child = listViewWrapper.getChildAt(i);
+ if (child != null && getPositionForView(listViewWrapper, child) == position) {
+ downView = child;
+ }
+ }
+ return downView;
+ }
+
+ /**
+ * Returns the {@link View} that represents the item for given position.
+ *
+ * @param absListView the ListView that should be examined
+ * @param position the position for which the {@code View} should be returned.
+ *
+ * @return the {@code View}, or {@code null} if the position is not currently visible.
+ */
+ @Nullable
+ public static View getViewForPosition(@NonNull final AbsListView absListView, final int position) {
+ int childCount = absListView.getChildCount();
+ View downView = null;
+ for (int i = 0; i < childCount && downView == null; i++) {
+ View child = absListView.getChildAt(i);
+ if (child != null && getPositionForView(absListView, child) == position) {
+ downView = child;
+ }
+ }
+ return downView;
+ }
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AnimatorUtil.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AnimatorUtil.java
new file mode 100644
index 00000000..78d2a3b1
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/AnimatorUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+
+import com.nineoldandroids.animation.Animator;
+
+public class AnimatorUtil {
+
+ private AnimatorUtil() {
+ }
+
+ /**
+ * Merges given Animators into one array.
+ */
+ @NonNull
+ public static Animator[] concatAnimators(@NonNull final Animator[] childAnimators, @NonNull final Animator[] animators, @NonNull final Animator alphaAnimator) {
+ Animator[] allAnimators = new Animator[childAnimators.length + animators.length + 1];
+ int i;
+
+ for (i = 0; i < childAnimators.length; ++i) {
+ allAnimators[i] = childAnimators[i];
+ }
+
+ for (Animator animator : animators) {
+ allAnimators[i] = animator;
+ ++i;
+ }
+
+ allAnimators[allAnimators.length - 1] = alphaAnimator;
+ return allAnimators;
+ }
+
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Insertable.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Insertable.java
new file mode 100644
index 00000000..08702b9c
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Insertable.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+
+/**
+ * An interface for inserting items at a certain index.
+ */
+public interface Insertable {
+
+ /**
+ * Will be called to insert given {@code item} at given {@code index} in the list.
+ *
+ * @param index the index the new item should be inserted at
+ * @param item the item to insert
+ */
+ void add(int index, @NonNull T item);
+}
\ No newline at end of file
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapper.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapper.java
new file mode 100644
index 00000000..3b65257b
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListAdapter;
+
+public interface ListViewWrapper {
+
+ @NonNull
+ ViewGroup getListView();
+
+ @Nullable
+ View getChildAt(int index);
+
+ int getFirstVisiblePosition();
+
+ int getLastVisiblePosition();
+
+ int getCount();
+
+ int getChildCount();
+
+ int getHeaderViewsCount();
+
+ int getPositionForView(@NonNull View view);
+
+ @Nullable
+ ListAdapter getAdapter();
+
+ void smoothScrollBy(int distance, int duration);
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapperSetter.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapperSetter.java
new file mode 100644
index 00000000..f784ac94
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/ListViewWrapperSetter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+import android.support.annotation.NonNull;
+
+public interface ListViewWrapperSetter {
+
+ void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper);
+}
diff --git a/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Swappable.java b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Swappable.java
new file mode 100644
index 00000000..c9d639d9
--- /dev/null
+++ b/lib-core/src/main/java/com/nhaarman/listviewanimations/util/Swappable.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.util;
+
+/**
+ * Interface, usually implemented by a {@link com.nhaarman.listviewanimations.BaseAdapterDecorator},
+ * that indicates that it can swap the visual position of two list items.
+ */
+public interface Swappable {
+
+ /**
+ * Swaps the item on the first adapter position with the item on the second adapter position.
+ * Be sure to call {@link android.widget.BaseAdapter#notifyDataSetChanged()} if appropriate when implementing this method.
+ *
+ * @param positionOne First adapter position.
+ * @param positionTwo Second adapter position.
+ */
+ void swapItems(int positionOne, int positionTwo);
+}
\ No newline at end of file
diff --git a/lib-manipulation/build.gradle b/lib-manipulation/build.gradle
new file mode 100644
index 00000000..2ddb3c2b
--- /dev/null
+++ b/lib-manipulation/build.gradle
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'github-pages'
+
+apply from: '../pushMaven.gradle'
+apply from: '../generateJavadoc.gradle'
+apply from: '../publishGhPages.gradle'
+
+dependencies {
+ compile project(':lib-core')
+
+ /* Test libraries */
+ androidTestCompile ('org.mockito:mockito-core:1.9.5') { exclude group: 'org.hamcrest' }
+ androidTestCompile ('junit:junit:4.11') { exclude group: 'org.hamcrest' }
+ androidTestCompile 'org.hamcrest:hamcrest-all:1.3'
+ androidTestCompile files('libs/dexmaker-1.1.jar')
+ androidTestCompile files('libs/dexmaker-mockito-1.1-custom.jar')
+}
+
+android {
+ compileSdkVersion 20
+ buildToolsVersion '20.0.0'
+
+ defaultConfig {
+ minSdkVersion 8
+ //noinspection OldTargetApi
+ targetSdkVersion 20
+ versionName project.VERSION_NAME
+ versionCode Integer.parseInt(new Date().format('yyyyMMddHH'))
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+
+ packagingOptions {
+ exclude 'LICENSE.txt'
+ }
+
+ lintOptions {
+ textReport true
+ textOutput 'stdout'
+ warningsAsErrors true
+ }
+}
+
diff --git a/lib-manipulation/gradle.properties b/lib-manipulation/gradle.properties
new file mode 100644
index 00000000..1a6b0fa1
--- /dev/null
+++ b/lib-manipulation/gradle.properties
@@ -0,0 +1,19 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+POM_NAME=ListViewAnimations Library
+POM_ARTIFACT_ID=lib-manipulation
+POM_PACKAGING=aar
\ No newline at end of file
diff --git a/lib-manipulation/libs/dexmaker-1.1.jar b/lib-manipulation/libs/dexmaker-1.1.jar
new file mode 100644
index 00000000..52a1a188
Binary files /dev/null and b/lib-manipulation/libs/dexmaker-1.1.jar differ
diff --git a/lib-manipulation/libs/dexmaker-mockito-1.1-custom.jar b/lib-manipulation/libs/dexmaker-mockito-1.1-custom.jar
new file mode 100644
index 00000000..adb688b1
Binary files /dev/null and b/lib-manipulation/libs/dexmaker-mockito-1.1-custom.jar differ
diff --git a/lib-manipulation/lint.xml b/lib-manipulation/lint.xml
new file mode 100644
index 00000000..65906e35
--- /dev/null
+++ b/lib-manipulation/lint.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-manipulation/pom.xml b/lib-manipulation/pom.xml
new file mode 100644
index 00000000..311a9c0e
--- /dev/null
+++ b/lib-manipulation/pom.xml
@@ -0,0 +1,200 @@
+
+
+
+ 4.0.0
+
+
+ com.nhaarman.listviewanimations
+ parent
+ 3.0.0-SNAPSHOT
+
+
+ lib-manipulation
+ apklib
+
+ ListViewAnimations (Manipulation Library)
+
+
+
+
+ com.google.android
+ android
+ provided
+
+
+
+
+ com.nhaarman.listviewanimations
+ lib-core
+ ${project.version}
+ apklib
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+ src/androidTest/java
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ true
+
+ ${project.basedir}/src/main/AndroidManifest.xml
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ package
+
+ attach-artifact
+
+
+
+
+ jar
+ ${project.build.directory}/${project.build.finalName}.jar
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+
+
+
+
+
+
+ org.eclipse.m2e
+ lifecycle-mapping
+ 1.0.0
+
+
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+
+ android-maven-plugin
+ [3.8.2,)
+
+ consume-aar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ release
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+ verify
+
+ package
+ true
+
+
+ ${project.build.directory}/${project.artifactId}.apklib
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-manipulation/proguard-project.txt b/lib-manipulation/proguard-project.txt
new file mode 100644
index 00000000..f2fe1559
--- /dev/null
+++ b/lib-manipulation/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# 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 *;
+#}
diff --git a/lib-manipulation/project.properties b/lib-manipulation/project.properties
new file mode 100644
index 00000000..bb38ab64
--- /dev/null
+++ b/lib-manipulation/project.properties
@@ -0,0 +1,32 @@
+#
+# Copyright 2014 Niek Haarman
+#
+# 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.
+#
+
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
+android.library.reference.1=../lib-core
diff --git a/library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/InsertQueueTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/animateaddition/InsertQueueTest.java
similarity index 78%
rename from library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/InsertQueueTest.java
rename to lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/animateaddition/InsertQueueTest.java
index 39a1ce92..2fa8d940 100644
--- a/library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/InsertQueueTest.java
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/animateaddition/InsertQueueTest.java
@@ -1,9 +1,25 @@
-package com.nhaarman.listviewanimations.itemmanipulation.tests;
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.animateaddition;
import android.util.Pair;
-import com.nhaarman.listviewanimations.itemmanipulation.AnimateAdditionAdapter;
-import com.nhaarman.listviewanimations.itemmanipulation.InsertQueue;
+import com.nhaarman.listviewanimations.itemmanipulation.animateaddition.InsertQueue;
+import com.nhaarman.listviewanimations.util.Insertable;
import junit.framework.TestCase;
@@ -13,17 +29,25 @@
import java.util.List;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-import static com.nhaarman.listviewanimations.itemmanipulation.tests.matchers.Matchers.*;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static com.nhaarman.listviewanimations.itemmanipulation.matchers.Matchers.*;
public class InsertQueueTest extends TestCase {
private InsertQueue mInsertQueue;
@Mock
- private AnimateAdditionAdapter.Insertable mInsertable;
+ private Insertable mInsertable;
@Override
protected void setUp() throws Exception {
@@ -31,7 +55,7 @@ protected void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mInsertQueue = new InsertQueue(mInsertable);
+ mInsertQueue = new InsertQueue<>(mInsertable);
}
/**
@@ -46,9 +70,9 @@ public void testInitialInsert() {
/**
* Test whether a second insert while the first insert is still active:
- * - doesn't insert the item
- * - doesn't activate the item
- * - queues the item.
+ * - doesn't insert the item
+ * - doesn't activate the item
+ * - queues the item.
*/
public void testSecondInsert() {
mInsertQueue.insert(0, 0);
@@ -73,8 +97,8 @@ public void testClearActive() {
/**
* Test whether inserting two items and then clearing the active items will
- * - activate the second inserted item,
- * - insert the second item
+ * - activate the second inserted item,
+ * - insert the second item
*/
public void testDequeueOneElement() {
mInsertQueue.insert(0, 0);
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewDragAndDropTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewDragAndDropTest.java
new file mode 100644
index 00000000..6f85dc94
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewDragAndDropTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.dragdrop;
+
+import android.support.annotation.NonNull;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
+
+import org.mockito.*;
+
+import static org.mockito.Mockito.*;
+
+public class DynamicListViewDragAndDropTest extends ActivityInstrumentationTestCase2 {
+
+ private DynamicListView mDynamicListView;
+
+ @Mock
+ private OnItemMovedListener mOnItemMovedListener;
+
+
+ public DynamicListViewDragAndDropTest() {
+ super(DynamicListViewTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception, InterruptedException {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ mDynamicListView = getActivity().getDynamicListView();
+ mDynamicListView.enableDragAndDrop();
+ mDynamicListView.setDraggableManager(new MyDraggableManager());
+ mDynamicListView.setOnItemMovedListener(mOnItemMovedListener);
+
+ getInstrumentation().waitForIdleSync();
+ Thread.sleep(5000);
+ }
+
+ public void testOnItemMovedListenerCalled() throws InterruptedException {
+ MotionEventUtils.dispatchDragMotionEvents(getInstrumentation(), mDynamicListView, 0, 1);
+ verify(mOnItemMovedListener).onItemMoved(0, 1);
+ }
+
+ public void testReverseOnItemMovedListenerCalled() throws InterruptedException {
+ MotionEventUtils.dispatchDragMotionEvents(getInstrumentation(), mDynamicListView, 2, 1);
+ verify(mOnItemMovedListener).onItemMoved(2, 1);
+ }
+
+ public void testOnItemMovedListenerCalledMultipleItems() throws InterruptedException {
+ MotionEventUtils.dispatchDragMotionEvents(getInstrumentation(), mDynamicListView, 1, 5);
+ verify(mOnItemMovedListener).onItemMoved(1, 5);
+ }
+
+ public void testScroll() throws InterruptedException {
+ MotionEventUtils.dispatchDragScrollDownMotionEvents(getInstrumentation(), mDynamicListView, 1);
+ verify(mOnItemMovedListener).onItemMoved(1, 19);
+ }
+
+ private static class MyDraggableManager implements DraggableManager {
+
+ @Override
+ public boolean isDraggable(@NonNull final View view, final int position, final float x, final float y) {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewTestActivity.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewTestActivity.java
new file mode 100644
index 00000000..f206be0b
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/DynamicListViewTestActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.dragdrop;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import com.nhaarman.listviewanimations.ArrayAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DynamicListViewTestActivity extends Activity {
+
+ private DynamicListView mListView;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+
+ mListView = new DynamicListView(this);
+ List integers = new ArrayList();
+ for (int i = 0; i < 20; i++) {
+ integers.add(i);
+ }
+
+ ListAdapter myListAdapter = new MyListAdapter(this, integers);
+ mListView.setAdapter(myListAdapter);
+
+ setContentView(mListView);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ int[] location = new int[2];
+ mListView.getLocationOnScreen(location);
+
+ ev = MotionEvent.obtain(ev.getDownTime(), ev.getEventTime(), ev.getAction(), ev.getX() - location[0], ev.getY() - location[1], ev.getMetaState());
+ boolean handled = mListView.onInterceptTouchEvent(ev);
+ if (!handled) {
+ handled = mListView.dispatchTouchEvent(ev);
+ }
+ if (!handled) {
+ handled = onTouchEvent(ev);
+ }
+ return handled;
+ }
+
+ public DynamicListView getDynamicListView() {
+ return mListView;
+ }
+
+ private static class MyListAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+
+ MyListAdapter(final Context context, final List items) {
+ super(items);
+ mContext = context;
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return getItem(position).hashCode();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = new TextView(mContext);
+ view.setTextSize(30);
+ }
+
+ view.setText("This is row number " + getItem(position));
+ return view;
+ }
+ }
+}
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/HoverDrawableTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/HoverDrawableTest.java
new file mode 100644
index 00000000..bbc3694b
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/HoverDrawableTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.dragdrop;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.mockito.*;
+
+import static org.hamcrest.MatcherAssert.*;
+import static org.hamcrest.Matchers.*;
+
+@SuppressWarnings("ALL")
+public class HoverDrawableTest extends AndroidTestCase {
+
+ private static final int START_Y = 29;
+
+ private HoverDrawable mHoverDrawable;
+
+ private View mView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ mView = new MyMockView(getContext());
+ mView.measure(0, 0);
+ mHoverDrawable = new HoverDrawable(mView, START_Y);
+ }
+
+ public void testInitialState() {
+ assertThat(mHoverDrawable.getDeltaY(), is(0));
+ assertThat(mHoverDrawable.getBounds().top, is(0));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(false));
+ }
+
+ public void testMovedState() {
+ MotionEvent motionEvent = createMotionEvent(START_Y + 10);
+ mHoverDrawable.handleMoveEvent(motionEvent);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(10));
+ assertThat(mHoverDrawable.getBounds().top, is(10));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(false));
+ }
+
+ public void testDoubleMovedState() {
+ MotionEvent motionEvent = createMotionEvent(START_Y + 10);
+ mHoverDrawable.handleMoveEvent(motionEvent);
+
+ MotionEvent motionEvent2 = createMotionEvent(START_Y + 20);
+ mHoverDrawable.handleMoveEvent(motionEvent2);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(20));
+ assertThat(mHoverDrawable.getBounds().top, is(20));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(false));
+ }
+
+ public void testReversedMovedState() {
+ MotionEvent motionEvent = createMotionEvent(START_Y - 10);
+ mHoverDrawable.handleMoveEvent(motionEvent);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(-10));
+ assertThat(mHoverDrawable.getBounds().top, is(-10));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(true));
+ }
+
+ public void testReversedDoubleMovedState() {
+ MotionEvent motionEvent = createMotionEvent(START_Y - 10);
+ mHoverDrawable.handleMoveEvent(motionEvent);
+
+ MotionEvent motionEvent2 = createMotionEvent(START_Y - 20);
+ mHoverDrawable.handleMoveEvent(motionEvent2);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(-20));
+ assertThat(mHoverDrawable.getBounds().top, is(-20));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(true));
+ }
+
+ public void testShift() {
+ MotionEvent motionEvent = createMotionEvent(START_Y - 10);
+ mHoverDrawable.handleMoveEvent(motionEvent);
+
+ mHoverDrawable.shift(10);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(0));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(false));
+ assertThat(mHoverDrawable.getBounds().top, is(-10));
+ }
+
+ public void testScroll() {
+ mHoverDrawable.onScroll(20);
+
+ assertThat(mHoverDrawable.getDeltaY(), is(-20));
+ assertThat(mHoverDrawable.isMovingUpwards(), is(true));
+ assertThat(mHoverDrawable.getBounds().top, is(0));
+ }
+
+ private static MotionEvent createMotionEvent(final float y) {
+ return MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, y, 0);
+ }
+
+ private static class MyMockView extends View {
+
+ MyMockView(final Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+ setMeasuredDimension(100, 100);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/MotionEventUtils.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/MotionEventUtils.java
new file mode 100644
index 00000000..e5b522a0
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/dragdrop/MotionEventUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.dragdrop;
+
+import android.annotation.TargetApi;
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AbsListView;
+
+
+import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@TargetApi(14)
+public class MotionEventUtils {
+
+ private MotionEventUtils() {
+ }
+
+ public static void dispatchDragMotionEvents(final Instrumentation instrumentation, final DynamicListView dynamicListView, final int fromPosition,
+ final int toPosition) throws InterruptedException {
+ int[] location = new int[2];
+ dynamicListView.getLocationOnScreen(location);
+
+ View view = dynamicListView.getChildAt(fromPosition);
+ float fromY = (int) (view.getY() + view.getHeight() / 2) + location[1];
+
+ View toView = dynamicListView.getChildAt(toPosition);
+ float toY = (int) toView.getY() + location[1];
+
+ toY += fromPosition < toPosition ? toView.getHeight() : 0;
+
+ List motionEvents = createMotionEvents(dynamicListView, fromY, toY);
+ dispatchMotionEvents(instrumentation, motionEvents, true);
+ }
+
+ public static void dispatchDragScrollDownMotionEvents(final Instrumentation instrumentation, final DynamicListView dynamicListView,
+ final int fromPosition) throws InterruptedException {
+ int[] location = new int[2];
+ dynamicListView.getLocationOnScreen(location);
+
+ View view = dynamicListView.getChildAt(fromPosition);
+ float fromY = (int) (view.getY() ) + location[1];
+
+ View toView = dynamicListView.getChildAt(dynamicListView.getLastVisiblePosition());
+ float toY = (int) (toView.getY() + toView.getHeight()) + location[1] + 2;
+
+ List motionEvents = createMotionEvents(dynamicListView, fromY, toY);
+ MotionEvent upEvent = motionEvents.remove(motionEvents.size() - 1);
+ dispatchMotionEvents(instrumentation, motionEvents, true);
+ Thread.sleep(10000);
+ dispatchMotionEvents(instrumentation, Arrays.asList(upEvent), true);
+ }
+
+ public static List createMotionEvents(final AbsListView absListView, final float fromY, final float toY) {
+ int x = (int) (absListView.getX() + absListView.getWidth() / 2);
+
+ List results = new ArrayList<>();
+ results.add(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, fromY, 0));
+
+ float diff = (toY - fromY) / 25;
+ float y = fromY;
+ for (int i = 0; i < 25; i++) {
+ y += diff;
+ results.add(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0));
+ }
+ results.add(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, toY, 0));
+
+ return results;
+ }
+
+ public static void dispatchMotionEvents(final Instrumentation instrumentation, final Iterable motionEvents, final boolean wait) throws InterruptedException {
+ for (final MotionEvent event : motionEvents) {
+ instrumentation.sendPointerSync(event);
+ Thread.sleep(100);
+ }
+
+ if (wait) {
+ /* We need to wait for the fling animation to complete */
+ Thread.sleep(1500);
+ }
+ }
+
+
+}
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/matchers/Matchers.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/matchers/Matchers.java
new file mode 100644
index 00000000..fa17b6e2
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/matchers/Matchers.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.matchers;
+
+import android.util.Pair;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class Matchers {
+
+ private Matchers() {
+ }
+
+ @Factory
+ public static Matcher> pairWithValues(final T first, final F second) {
+ return new PairWithValues<>(first, second);
+ }
+
+ @Factory
+ public static Matcher atomicIntegerWithValue(final int value) {
+ return new AtomicIntegerWithValue(value);
+ }
+
+ private static class PairWithValues extends TypeSafeMatcher> {
+
+ private final T mFirst;
+ private final F mSecond;
+
+ private PairWithValues(final T first, final F second) {
+ mFirst = first;
+ mSecond = second;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("Dunno!");
+ }
+
+ @Override
+ protected boolean matchesSafely(final Pair item) {
+ return item.first.equals(mFirst) && item.second.equals(mSecond);
+ }
+ }
+
+ private static class AtomicIntegerWithValue extends TypeSafeMatcher {
+
+ private final int mValue;
+
+ AtomicIntegerWithValue(final int value) {
+ mValue = value;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText(String.valueOf(mValue));
+ }
+
+ @Override
+ protected boolean matchesSafely(final AtomicInteger item) {
+ return mValue == item.intValue();
+ }
+ }
+}
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/MotionEventUtils.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/MotionEventUtils.java
new file mode 100644
index 00000000..baa328e3
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/MotionEventUtils.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.app.Activity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AbsListView;
+
+import com.nineoldandroids.view.ViewHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MotionEventUtils {
+
+ private MotionEventUtils() {
+ }
+
+ public static void dispatchSwipeMotionEventsAndWait(final Activity activity, final AbsListView absListView, final int position) throws InterruptedException {
+ dispatchMotionEventsAndWait(activity, absListView, createSwipeMotionEvents(absListView, position));
+ }
+
+ public static void dispatchReverseSwipeMotionEventsAndWait(final Activity activity, final AbsListView absListView, final int position) throws InterruptedException {
+ dispatchMotionEventsAndWait(activity, absListView, createReverseSwipeMotionEvents(absListView, position));
+ }
+
+ public static void dispatchMotionEventsAndWait(final Activity activity, final View view, final Iterable motionEvents) throws InterruptedException {
+ dispatchMotionEvents(activity, view, motionEvents, true);
+ }
+
+ public static void dispatchSwipeMotionEvents(final Activity activity, final AbsListView absListView, final int position) throws InterruptedException {
+ dispatchMotionEvents(activity, absListView, createSwipeMotionEvents(absListView, position));
+ }
+
+ public static void dispatchReverseSwipeMotionEvents(final Activity activity, final AbsListView absListView, final int position) throws InterruptedException {
+ dispatchMotionEvents(activity, absListView, createReverseSwipeMotionEvents(absListView, position));
+ }
+
+ public static void dispatchMotionEvents(final Activity activity, final View view, final Iterable motionEvents) throws InterruptedException {
+ dispatchMotionEvents(activity, view, motionEvents, false);
+ }
+
+ public static List createSwipeMotionEvents(final AbsListView absListView, final int position) {
+ int viewWidth = absListView.getWidth();
+ return createMotionEvents(absListView, position, 10, viewWidth - 10);
+ }
+
+ public static List createReverseSwipeMotionEvents(final AbsListView absListView, final int position) {
+ int viewWidth = absListView.getWidth();
+ return createMotionEvents(absListView, position, viewWidth - 10, 10);
+ }
+
+ public static List createMotionEvents(final AbsListView absListView, final int position, final float fromX, final float toX) {
+ int[] listViewCoords = new int[2];
+ absListView.getLocationOnScreen(listViewCoords);
+
+ View view = absListView.getChildAt(position);
+ int y = (int) (ViewHelper.getY(view) + view.getHeight() / 2) + listViewCoords[1];
+
+ List results = new ArrayList<>();
+ results.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, fromX, y, 0));
+
+ float diff = fromX - toX;
+ for (int i = 1; i < 10; i++) {
+ float x = fromX + diff / 10 * i;
+ results.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, x, y, 0));
+ }
+ results.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, toX, y, 0));
+
+ return results;
+ }
+
+ private static void dispatchMotionEvents(final Activity activity, final View view, final Iterable motionEvents, final boolean wait) throws InterruptedException {
+ for (final MotionEvent event : motionEvents) {
+ activity.runOnUiThread(new DispatchTouchEventRunnable(event, view));
+ Thread.sleep(100);
+ }
+
+ if (wait) {
+ /* We need to wait for the fling animation to complete */
+ Thread.sleep(1500);
+ }
+ }
+
+ private static class DispatchTouchEventRunnable implements Runnable {
+
+ private final MotionEvent mEvent;
+ private final View mView;
+
+ private DispatchTouchEventRunnable(final MotionEvent event, final View view) {
+ mEvent = event;
+ mView = view;
+ }
+
+ @Override
+ public void run() {
+ mView.dispatchTouchEvent(mEvent);
+ }
+ }
+
+}
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListenerTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListenerTest.java
new file mode 100644
index 00000000..bac7677e
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListenerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.widget.AbsListView;
+
+import com.nhaarman.listviewanimations.util.AbsListViewWrapper;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.MotionEventUtils.dispatchSwipeMotionEvents;
+import static com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.MotionEventUtils.dispatchSwipeMotionEventsAndWait;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@SuppressWarnings("AnonymousInnerClass")
+public class SwipeDismissTouchListenerTest extends ActivityInstrumentationTestCase2 {
+
+ /**
+ * An Activity hosting a ListView with items.
+ */
+ private SwipeTouchListenerTestActivity mActivity;
+
+ /**
+ * The AbsListView hosted by mActivity.
+ */
+ private AbsListView mAbsListView;
+
+ /**
+ * The SwipeTouchListener under test.
+ */
+ private SwipeDismissTouchListener mSwipeTouchListener;
+
+ @Mock
+ private OnDismissCallback mOnDismissCallback;
+
+ public SwipeDismissTouchListenerTest() {
+ super(SwipeTouchListenerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ mActivity = getActivity();
+ mAbsListView = mActivity.getAbsListView();
+
+ mSwipeTouchListener = new SwipeDismissTouchListener(new AbsListViewWrapper(mAbsListView), mOnDismissCallback);
+ mAbsListView.setOnTouchListener(mSwipeTouchListener);
+
+ getInstrumentation().waitForIdleSync();
+ }
+
+ /**
+ * Tests whether dismissing an item triggers a call to OnDismissCallback#onDismiss.
+ */
+ public void testSimpleDismiss() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ verify(mOnDismissCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{0}));
+ }
+
+ /**
+ * Tests whether dismissing the first and second items triggers a correct call to OnDismissCallback#onDismiss.
+ */
+ public void testDoubleDismiss() throws InterruptedException {
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 0);
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 1);
+
+ verify(mOnDismissCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{1, 0}));
+ }
+
+ /**
+ * Tests whether dismissing mixed positions triggers a correct call to OnDismissCallback#onDismiss.
+ */
+ public void testComplexDismiss() throws InterruptedException {
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 0);
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 3);
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 2);
+
+ verify(mOnDismissCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{3, 2, 0}));
+ }
+
+
+ /**
+ * Tests whether calling SwipeTouchListener#fling triggers a call to OnDismissCallback#onDismiss.
+ */
+ public void testFling() throws InterruptedException {
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mSwipeTouchListener.fling(0);
+ }
+ });
+
+ /* We need to wait for the animation to complete */
+ Thread.sleep(1500);
+
+ verify(mOnDismissCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{0}));
+ }
+
+ /**
+ * Tests whether calling SwipeTouchListener#dismiss triggers a call to OnDismissCallback#onDismiss.
+ */
+ public void testDismiss() throws InterruptedException {
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mSwipeTouchListener.dismiss(0);
+ }
+ });
+
+ /* We need to wait for the animation to complete */
+ Thread.sleep(1500);
+
+ verify(mOnDismissCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{0}));
+ }
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTest.java
new file mode 100644
index 00000000..ec0927c1
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.support.annotation.NonNull;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+
+import com.nhaarman.listviewanimations.util.AbsListViewWrapper;
+
+import java.util.List;
+
+import static com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.MotionEventUtils.dispatchSwipeMotionEventsAndWait;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+
+@SuppressWarnings({"AnonymousInnerClass", "AnonymousInnerClassMayBeStatic"})
+public class SwipeTouchListenerTest extends ActivityInstrumentationTestCase2 {
+
+ /**
+ * The SwipeTouchListener under test.
+ */
+ private TestSwipeTouchListener mSwipeTouchListener;
+
+ /**
+ * An Activity hosting a ListView with items.
+ */
+ private SwipeTouchListenerTestActivity mActivity;
+
+ /**
+ * The AbsListView that is hosted in mActivity.
+ */
+ private AbsListView mAbsListView;
+
+ /**
+ * The width of the AbsListView.
+ */
+ private float mViewWidth;
+
+ public SwipeTouchListenerTest() {
+ super(SwipeTouchListenerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mActivity = getActivity();
+ mAbsListView = mActivity.getAbsListView();
+ mViewWidth = mAbsListView.getWidth();
+
+ mSwipeTouchListener = new TestSwipeTouchListener(new AbsListViewWrapper(mAbsListView));
+ mAbsListView.setOnTouchListener(mSwipeTouchListener);
+
+ getInstrumentation().waitForIdleSync();
+ }
+
+
+ /**
+ * Tests whether retrieving the AbsListView yields the original AbsListView that was set.
+ */
+ public void testAbsListViewSet() {
+ assertThat(mSwipeTouchListener.getListViewWrapper().getListView(), is((ViewGroup) mAbsListView));
+ }
+
+ /**
+ * Tests whether swiping the first View triggers a call to SwipeTouchListener#afterViewFling.
+ */
+ public void testSwipeFirstViewCallback() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(true));
+ assertThat(mSwipeTouchListener.position, is(0));
+ }
+
+ /**
+ * Tests whether swiping the first View from right to left triggers a call to SwipeTouchListener#afterViewFling.
+ */
+ public void testReverseSwipeFirstViewCallback() throws InterruptedException {
+ MotionEventUtils.dispatchReverseSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(true));
+ assertThat(mSwipeTouchListener.position, is(0));
+ }
+
+ /**
+ * Tests whether swiping the last View triggers a call to SwipeTouchListener#afterViewFling.
+ */
+ public void testSwipeLastViewCallback() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, mAbsListView.getLastVisiblePosition());
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(true));
+ assertThat(mSwipeTouchListener.position, is(mAbsListView.getLastVisiblePosition()));
+ }
+
+ /**
+ * Tests whether swiping shorter than half of the view width doesn't trigger a call to SwipeTouchLister#afterViewFling.
+ */
+ public void testShortSwipe() throws InterruptedException {
+ List motionEvents = MotionEventUtils.createMotionEvents(mAbsListView, 0, 10, mViewWidth / 2 - mViewWidth / 10);
+ MotionEventUtils.dispatchMotionEventsAndWait(mActivity, mAbsListView, motionEvents);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(false));
+ }
+
+ /**
+ * Tests whether swiping shorter than half of the view width from right to left doesn't trigger a call to SwipeTouchLister#afterViewFling.
+ */
+ public void testReverseShortSwipe() throws InterruptedException {
+ List motionEvents = MotionEventUtils.createMotionEvents(mAbsListView, 0, mViewWidth - 10, mViewWidth / 2 + mViewWidth / 10);
+ MotionEventUtils.dispatchMotionEventsAndWait(mActivity, mAbsListView, motionEvents);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(false));
+ }
+
+ /**
+ * Tests whether calling SwipeTouchListener#fling(int) triggers a call to SwipeTouchListener#afterViewFling.
+ */
+ public void testFling() throws InterruptedException {
+ mActivity.runOnUiThread(
+ new Runnable() {
+
+ @Override
+ public void run() {
+ mSwipeTouchListener.fling(0);
+ }
+ }
+ );
+
+ Thread.sleep(1000);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(true));
+ assertThat(mSwipeTouchListener.position, is(0));
+ }
+
+ /**
+ * Tests whether trying to dismiss an item that is specified not to be dismissable doesn't trigger a call to SwipeTouchListener#afterViewFling.
+ */
+ public void testDismissableManager() throws InterruptedException {
+ mSwipeTouchListener.setDismissableManager(
+ new DismissableManager() {
+
+ @Override
+ public boolean isDismissable(final long id, final int position) {
+ return false;
+ }
+ }
+ );
+
+ List motionEvents = MotionEventUtils.createMotionEvents(mAbsListView, 0, 10, mViewWidth - 10);
+ MotionEventUtils.dispatchMotionEvents(mActivity, mAbsListView, motionEvents);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(false));
+ }
+
+ /**
+ * Tests whether the isSwiping method returns proper values.
+ */
+ public void testIsSwiping() throws InterruptedException {
+ List motionEvents = MotionEventUtils.createMotionEvents(mAbsListView, 0, 10, mViewWidth - 10);
+
+ assertThat(mSwipeTouchListener.isSwiping(), is(false));
+
+ /* Send first half of the MotionEvents */
+ MotionEventUtils.dispatchMotionEvents(mActivity, mAbsListView, motionEvents.subList(0, motionEvents.size() / 2));
+
+ assertThat(mSwipeTouchListener.isSwiping(), is(true));
+
+ /* Send second half of the MotionEvents */
+ MotionEventUtils.dispatchMotionEvents(mActivity, mAbsListView, motionEvents.subList(motionEvents.size() / 2, motionEvents.size()));
+
+ assertThat(mSwipeTouchListener.isSwiping(), is(false));
+ }
+
+ /**
+ * Test whether disabling swipe and swiping an item does not trigger SwipeTouchListener#afterViewFling, and enabling it again does trigger the call.
+ */
+ public void testEnableDisableSwipe() throws InterruptedException {
+ mSwipeTouchListener.disableSwipe();
+
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(false));
+
+ mSwipeTouchListener.enableSwipe();
+
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ assertThat(mSwipeTouchListener.afterViewFlingCalled, is(true));
+ }
+
+ private static class TestSwipeTouchListener extends SwipeTouchListener {
+
+ boolean afterViewFlingCalled;
+ int position;
+
+ TestSwipeTouchListener(final AbsListViewWrapper absListViewWrapper) {
+ super(absListViewWrapper);
+ }
+
+ @Override
+ protected void afterViewFling(@NonNull final View view, final int position) {
+ afterViewFlingCalled = true;
+ this.position = position;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTestActivity.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTestActivity.java
new file mode 100644
index 00000000..790cfd1d
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListenerTestActivity.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.nhaarman.listviewanimations.ArrayAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SwipeTouchListenerTestActivity extends Activity {
+
+ private ListView mListView;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Window window = getWindow();
+ window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+
+ mListView = new ListView(this);
+ List integers = new ArrayList();
+ for (int i = 0; i < 20; i++) {
+ integers.add(i);
+ }
+
+ ListAdapter myListAdapter = new MyListAdapter(this, integers);
+ mListView.setAdapter(myListAdapter);
+
+ setContentView(mListView);
+ }
+
+ public AbsListView getAbsListView() {
+ return mListView;
+ }
+
+ private static class MyListAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+
+ MyListAdapter(final Context context, final List items) {
+ super(items);
+ mContext = context;
+ }
+
+ @Override
+ public long getItemId(final int location) {
+ return getItem(location).hashCode();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(final int position, final View convertView, final ViewGroup parent) {
+ TextView view = (TextView) convertView;
+ if (view == null) {
+ view = new TextView(mContext);
+ view.setTextSize(30);
+ }
+
+ view.setText("This is row number " + getItem(position));
+ return view;
+ }
+ }
+}
diff --git a/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListenerTest.java b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListenerTest.java
new file mode 100644
index 00000000..13c6ce5e
--- /dev/null
+++ b/lib-manipulation/src/androidTest/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListenerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+import android.widget.AbsListView;
+
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeTouchListenerTestActivity;
+import com.nhaarman.listviewanimations.util.AbsListViewWrapper;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.MotionEventUtils.dispatchSwipeMotionEvents;
+import static com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.MotionEventUtils.dispatchSwipeMotionEventsAndWait;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SwipeUndoTouchListenerTest extends ActivityInstrumentationTestCase2 {
+
+ /**
+ * An Activity hosting a ListView with items.
+ */
+ private SwipeTouchListenerTestActivity mActivity;
+
+ /**
+ * The AbsListView that is hosted in mActivity.
+ */
+ private AbsListView mAbsListView;
+
+ @Mock
+ private UndoCallback mUndoCallback;
+
+
+ public SwipeUndoTouchListenerTest() {
+ super(SwipeTouchListenerTestActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+ when(mUndoCallback.getUndoView(any(View.class))).thenReturn(new View(getActivity()));
+ when(mUndoCallback.getPrimaryView(any(View.class))).thenReturn(new View(getActivity()));
+
+ mActivity = getActivity();
+ mAbsListView = mActivity.getAbsListView();
+
+ View.OnTouchListener swipeUndoTouchListener = new SwipeUndoTouchListener(new AbsListViewWrapper(mAbsListView), mUndoCallback);
+ mAbsListView.setOnTouchListener(swipeUndoTouchListener);
+
+ getInstrumentation().waitForIdleSync();
+ }
+
+ /**
+ * Tests whether swiping an item once triggers UndoCallback#onUndoShown.
+ */
+ public void testUndoShown() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ verify(mUndoCallback).onUndoShown(any(View.class), eq(0));
+ }
+
+ /**
+ * Tests whether swiping an item twice triggers UndoCallback#onDismiss.
+ */
+ public void testDismiss() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ verify(mUndoCallback).onUndoShown(any(View.class), eq(0));
+ verify(mUndoCallback, never()).onDismiss(any(View.class), anyInt());
+
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ verify(mUndoCallback).onDismiss(any(View.class), eq(0));
+ }
+
+ /**
+ * Tests whether swiping multiple items triggers onUndoShown, but not onDismiss.
+ */
+ public void testMultipleUndo() throws InterruptedException {
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 0);
+
+ verify(mUndoCallback).onUndoShown(any(View.class), eq(0));
+
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 1);
+
+ verify(mUndoCallback).onUndoShown(any(View.class), eq(1));
+
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 2);
+
+ verify(mUndoCallback).onUndoShown(any(View.class), eq(2));
+
+ verify(mUndoCallback, never()).onDismiss(any(View.class), anyInt());
+ }
+
+ /**
+ * Tests whether multiple dismisses are correctly handled.
+ */
+ public void testMultipleDismisses() throws InterruptedException {
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 0);
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 1);
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 2);
+
+ verify(mUndoCallback, times(3)).onUndoShown(any(View.class), anyInt());
+ verify(mUndoCallback, never()).onDismiss(any(View.class), anyInt());
+
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 0);
+ dispatchSwipeMotionEvents(mActivity, mAbsListView, 1);
+ dispatchSwipeMotionEventsAndWait(mActivity, mAbsListView, 2);
+
+ verify(mUndoCallback, times(3)).onDismiss(any(View.class), anyInt());
+ verify(mUndoCallback).onDismiss(eq(mAbsListView), aryEq(new int[]{2, 1, 0}));
+ }
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/main/AndroidManifest.xml b/lib-manipulation/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..6b6eccb3
--- /dev/null
+++ b/lib-manipulation/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/DynamicListView.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/DynamicListView.java
new file mode 100644
index 00000000..df75e694
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/DynamicListView.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.itemmanipulation.animateaddition.AnimateAdditionAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DragAndDropHandler;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DraggableManager;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DynamicListViewWrapper;
+import com.nhaarman.listviewanimations.itemmanipulation.dragdrop.OnItemMovedListener;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissTouchListener;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeTouchListener;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.SwipeUndoAdapter;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.SwipeUndoTouchListener;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoCallback;
+import com.nhaarman.listviewanimations.util.Insertable;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * A {@link android.widget.ListView} implementation which provides the following functionality:
+ *
+ *
Drag and drop
+ *
Swipe to dismiss
+ *
Swipe to dismiss with contextual undo
+ *
Animate addition
+ *
+ */
+public class DynamicListView extends ListView {
+
+ @NonNull
+ private final MyOnScrollListener mMyOnScrollListener;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.dragdrop.DragAndDropHandler}
+ * that will handle drag and drop functionality, if set.
+ */
+ @Nullable
+ private DragAndDropHandler mDragAndDropHandler;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeTouchListener}
+ * that will handle swipe movement functionality, if set.
+ */
+ @Nullable
+ private SwipeTouchListener mSwipeTouchListener;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.TouchEventHandler}
+ * that is currently actively consuming {@code MotionEvent}s.
+ */
+ @Nullable
+ private TouchEventHandler mCurrentHandlingTouchEventHandler;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.animateaddition.AnimateAdditionAdapter}
+ * that is possibly set to animate insertions.
+ */
+ @Nullable
+ private AnimateAdditionAdapter
Default behavior: the whole
@@ -121,14 +144,15 @@ public void setLimit(final int limit) {
}
/**
- * Set the {@link com.nhaarman.listviewanimations.itemmanipulation.ExpandCollapseListener} that should be notified of expand / collapse events.
+ * Set the {@link ExpandCollapseListener} that should be notified of expand / collapse events.
*/
- public void setExpandCollapseListener(final ExpandCollapseListener expandCollapseListener) {
+ public void setExpandCollapseListener(@Nullable final ExpandCollapseListener expandCollapseListener) {
mExpandCollapseListener = expandCollapseListener;
}
@Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
+ @NonNull
+ public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
ViewGroup view = (ViewGroup) convertView;
ViewHolder viewHolder;
@@ -145,7 +169,7 @@ public View getView(final int position, final View convertView, final ViewGroup
}
View titleView = getTitleView(position, viewHolder.titleView, viewHolder.titleParent);
- if (titleView != viewHolder.titleView) {
+ if (!titleView.equals(viewHolder.titleView)) {
viewHolder.titleParent.removeAllViews();
viewHolder.titleParent.addView(titleView);
@@ -158,7 +182,7 @@ public View getView(final int position, final View convertView, final ViewGroup
viewHolder.titleView = titleView;
View contentView = getContentView(position, viewHolder.contentView, viewHolder.contentParent);
- if (contentView != viewHolder.contentView) {
+ if (!contentView.equals(viewHolder.contentView)) {
viewHolder.contentParent.removeAllViews();
viewHolder.contentParent.addView(contentView);
}
@@ -167,25 +191,13 @@ public View getView(final int position, final View convertView, final ViewGroup
viewHolder.contentParent.setVisibility(mExpandedIds.contains(getItemId(position)) ? View.VISIBLE : View.GONE);
viewHolder.contentParent.setTag(getItemId(position));
- ViewGroup.LayoutParams layoutParams = viewHolder.contentParent.getLayoutParams();
+ LayoutParams layoutParams = viewHolder.contentParent.getLayoutParams();
layoutParams.height = LayoutParams.WRAP_CONTENT;
viewHolder.contentParent.setLayoutParams(layoutParams);
return view;
}
- private ViewGroup createView(final ViewGroup parent) {
- ViewGroup view;
-
- if (mViewLayoutResId == 0) {
- view = new RootView(mContext);
- } else {
- view = (ViewGroup) LayoutInflater.from(mContext).inflate(mViewLayoutResId, parent, false);
- }
-
- return view;
- }
-
/**
* Get a View that displays the title of the data at the specified
* position in the data set. You can either create a View manually or
@@ -202,10 +214,12 @@ private ViewGroup createView(final ViewGroup parent) {
* using. If it is not possible to convert this view to display
* the correct data, this method can create a new view.
* @param parent The parent that this view will eventually be attached to
+ *
* @return A View corresponding to the title of the data at the specified
* position.
*/
- public abstract View getTitleView(int position, View convertView, ViewGroup parent);
+ @NonNull
+ public abstract View getTitleView(int position, @Nullable View convertView, @NonNull ViewGroup parent);
/**
* Get a View that displays the content of the data at the specified
@@ -223,15 +237,18 @@ private ViewGroup createView(final ViewGroup parent) {
* using. If it is not possible to convert this view to display
* the correct data, this method can create a new view.
* @param parent The parent that this view will eventually be attached to
+ *
* @return A View corresponding to the content of the data at the specified
* position.
*/
- public abstract View getContentView(int position, View convertView, ViewGroup parent);
+ @NonNull
+ public abstract View getContentView(int position, @Nullable View convertView, @NonNull ViewGroup parent);
/**
* Indicates if the item at the specified position is expanded.
*
* @param position Index of the view whose state we want.
+ *
* @return true if the view is expanded, false otherwise.
*/
public boolean isExpanded(final int position) {
@@ -243,15 +260,19 @@ public boolean isExpanded(final int position) {
* Return the title view at the specified position.
*
* @param position Index of the view we want.
+ *
* @return the view if it exist, null otherwise.
*/
+ @Nullable
public View getTitleView(final int position) {
View titleView = null;
View parentView = findViewForPosition(position);
- Object tag = parentView.getTag();
- if (tag instanceof ViewHolder) {
- titleView = ((ViewHolder) tag).titleView;
+ if (parentView != null) {
+ Object tag = parentView.getTag();
+ if (tag instanceof ViewHolder) {
+ titleView = ((ViewHolder) tag).titleView;
+ }
}
return titleView;
@@ -261,8 +282,10 @@ public View getTitleView(final int position) {
* Return the content view at the specified position.
*
* @param position Index of the view we want.
+ *
* @return the view if it exist, null otherwise.
*/
+ @Nullable
public View getContentView(final int position) {
View contentView = null;
@@ -281,7 +304,7 @@ public View getContentView(final int position) {
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
- Set removedIds = new HashSet(mExpandedIds);
+ Collection removedIds = new HashSet<>(mExpandedIds);
for (int i = 0; i < getCount(); ++i) {
long id = getItemId(i);
@@ -291,26 +314,6 @@ public void notifyDataSetChanged() {
mExpandedIds.removeAll(removedIds);
}
- /**
- * Return the content parent at the specified position.
- *
- * @param position Index of the view we want.
- * @return the view if it exist, null otherwise.
- */
- private View getContentParent(final int position) {
- View contentParent = null;
-
- View parentView = findViewForPosition(position);
- if (parentView != null) {
- Object tag = parentView.getTag();
- if (tag instanceof ViewHolder) {
- contentParent = ((ViewHolder) tag).contentParent;
- }
- }
-
- return contentParent;
- }
-
/**
* Expand the view at given position. Will do nothing if the view is already expanded.
*
@@ -339,11 +342,72 @@ public void collapse(final int position) {
toggle(position);
}
+ /**
+ * Toggle the {@link android.view.View} at given position, ignores header or footer Views.
+ *
+ * @param position the position of the view to toggle.
+ */
+ public void toggle(final int position) {
+ long itemId = getItemId(position);
+ boolean isExpanded = mExpandedIds.contains(itemId);
+
+ View contentParent = getContentParent(position);
+ if (contentParent != null) {
+ toggle(contentParent);
+ }
+
+ if (contentParent == null && isExpanded) {
+ mExpandedIds.remove(itemId);
+ } else if (contentParent == null) {
+ mExpandedIds.add(itemId);
+ }
+ }
+
+ @NonNull
+ private ViewGroup createView(@NonNull final ViewGroup parent) {
+ ViewGroup view;
+
+ if (mViewLayoutResId == 0) {
+ view = new RootView(mContext);
+ } else {
+ view = (ViewGroup) LayoutInflater.from(mContext).inflate(mViewLayoutResId, parent, false);
+ }
+
+ return view;
+ }
+
+ /**
+ * Return the content parent at the specified position.
+ *
+ * @param position Index of the view we want.
+ *
+ * @return the view if it exist, null otherwise.
+ */
+ @Nullable
+ private View getContentParent(final int position) {
+ View contentParent = null;
+
+ View parentView = findViewForPosition(position);
+ if (parentView != null) {
+ Object tag = parentView.getTag();
+ if (tag instanceof ViewHolder) {
+ contentParent = ((ViewHolder) tag).contentParent;
+ }
+ }
+
+ return contentParent;
+ }
+
+ @Nullable
private View findViewForPosition(final int position) {
+ if (mListViewWrapper == null) {
+ throw new IllegalStateException("Call setAbsListView on this ExpanableListItemAdapter!");
+ }
+
View result = null;
- for (int i = 0; i < mAbsListView.getChildCount() && result == null; i++) {
- View childView = mAbsListView.getChildAt(i);
- if (AdapterViewUtil.getPositionForView(mAbsListView, childView) == position) {
+ for (int i = 0; i < mListViewWrapper.getChildCount() && result == null; i++) {
+ View childView = mListViewWrapper.getChildAt(i);
+ if (childView != null && AdapterViewUtil.getPositionForView(mListViewWrapper, childView) == position) {
result = childView;
}
}
@@ -359,28 +423,12 @@ private int findPositionForId(final long id) {
return -1;
}
- /**
- * Toggle the {@link View} at given position, ignores header or footer Views.
- *
- * @param position the position of the view to toggle.
- */
- public void toggle(final int position) {
- long itemId = getItemId(position);
- boolean isExpanded = mExpandedIds.contains(itemId);
-
- View contentParent = getContentParent(position);
- if (contentParent != null) {
- toggle(contentParent);
+ private void toggle(@NonNull final View contentParent) {
+ if (mListViewWrapper == null) {
+ throw new IllegalStateException("No ListView set!");
}
- if (contentParent == null && isExpanded) {
- mExpandedIds.remove(itemId);
- } else if (contentParent == null && !isExpanded) {
- mExpandedIds.add(itemId);
- }
- }
- private void toggle(final View contentParent) {
boolean isVisible = contentParent.getVisibility() == View.VISIBLE;
boolean shouldCollapseOther = !isVisible && mLimit > 0 && mExpandedIds.size() >= mLimit;
if (shouldCollapseOther) {
@@ -409,7 +457,7 @@ private void toggle(final View contentParent) {
}
} else {
- ExpandCollapseHelper.animateExpanding(contentParent, mAbsListView);
+ ExpandCollapseHelper.animateExpanding(contentParent, mListViewWrapper);
mExpandedIds.add(id);
if (mExpandCollapseListener != null) {
@@ -418,18 +466,11 @@ private void toggle(final View contentParent) {
}
}
- private class TitleViewOnClickListener implements View.OnClickListener {
-
- private final View mContentParent;
+ public interface ExpandCollapseListener {
- private TitleViewOnClickListener(final View contentParent) {
- mContentParent = contentParent;
- }
+ void onItemExpanded(int position);
- @Override
- public void onClick(final View view) {
- toggle(mContentParent);
- }
+ void onItemCollapsed(int position);
}
private static class RootView extends LinearLayout {
@@ -437,7 +478,7 @@ private static class RootView extends LinearLayout {
private ViewGroup mTitleViewGroup;
private ViewGroup mContentViewGroup;
- public RootView(final Context context) {
+ private RootView(@NonNull final Context context) {
super(context);
init();
}
@@ -468,17 +509,19 @@ public static void animateCollapsing(final View view) {
int origHeight = view.getHeight();
ValueAnimator animator = createHeightAnimator(view, origHeight, 0);
- animator.addListener(new AnimatorListenerAdapter() {
+ animator.addListener(
+ new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animator) {
- view.setVisibility(View.GONE);
- }
- });
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ view.setVisibility(View.GONE);
+ }
+ }
+ );
animator.start();
}
- public static void animateExpanding(final View view, final AbsListView listView) {
+ public static void animateExpanding(@NonNull final View view, @NonNull final ListViewWrapper listViewWrapper) {
view.setVisibility(View.VISIBLE);
View parent = (View) view.getParent();
@@ -487,49 +530,68 @@ public static void animateExpanding(final View view, final AbsListView listView)
view.measure(widthSpec, heightSpec);
ValueAnimator animator = createHeightAnimator(view, 0, view.getMeasuredHeight());
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- final int listViewHeight = listView.getHeight();
- final int listViewBottomPadding = listView.getPaddingBottom();
- final View v = findDirectChild(view, listView);
-
- @Override
- public void onAnimationUpdate(final ValueAnimator valueAnimator) {
- final int bottom = v.getBottom();
- if (bottom > listViewHeight) {
- final int top = v.getTop();
- if (top > 0) {
- listView.smoothScrollBy(Math.min(bottom - listViewHeight + listViewBottomPadding, top), 0);
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ final int listViewHeight = listViewWrapper.getListView().getHeight();
+ final int listViewBottomPadding = listViewWrapper.getListView().getPaddingBottom();
+ final View v = findDirectChild(view, listViewWrapper.getListView());
+
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ final int bottom = v.getBottom();
+ if (bottom > listViewHeight) {
+ final int top = v.getTop();
+ if (top > 0) {
+ listViewWrapper.smoothScrollBy(Math.min(bottom - listViewHeight + listViewBottomPadding, top), 0);
+ }
+ }
}
}
- }
- });
+ );
animator.start();
}
- private static View findDirectChild(final View view, final AbsListView listView) {
+ public static ValueAnimator createHeightAnimator(final View view, final int start, final int end) {
+ ValueAnimator animator = ValueAnimator.ofInt(start, end);
+ animator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ int value = (Integer) animation.getAnimatedValue();
+
+ LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.height = value;
+ view.setLayoutParams(layoutParams);
+ }
+ }
+ );
+ return animator;
+ }
+
+ @NonNull
+ private static View findDirectChild(@NonNull final View view, @NonNull final ViewGroup listView) {
View result = view;
View parent = (View) result.getParent();
- while (parent != listView) {
+ while (!parent.equals(listView)) {
result = parent;
parent = (View) result.getParent();
}
return result;
}
+ }
- public static ValueAnimator createHeightAnimator(final View view, final int start, final int end) {
- ValueAnimator animator = ValueAnimator.ofInt(start, end);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ private class TitleViewOnClickListener implements View.OnClickListener {
- @Override
- public void onAnimationUpdate(final ValueAnimator valueAnimator) {
- int value = (Integer) valueAnimator.getAnimatedValue();
+ private final View mContentParent;
- ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
- layoutParams.height = value;
- view.setLayoutParams(layoutParams);
- }
- });
- return animator;
+ private TitleViewOnClickListener(final View contentParent) {
+ mContentParent = contentParent;
+ }
+
+ @Override
+ public void onClick(final View view) {
+ toggle(mContentParent);
}
}
}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java
new file mode 100644
index 00000000..c8dd9e5e
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+/**
+ * An interface to specify whether certain items can or cannot be dismissed.
+ */
+public interface DismissableManager {
+
+ /**
+ * Returns whether the item for given id and position can be dismissed.
+ * @param id the id of the item.
+ * @param position the position of the item.
+ * @return true if the item can be dismissed, false otherwise.
+ */
+ boolean isDismissable(long id, int position);
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/OnDismissCallback.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/OnDismissCallback.java
new file mode 100644
index 00000000..6bb5e7ec
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/OnDismissCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+
+/**
+ * A callback interface used to inform its client about a successful dismissal of one or more list item positions.
+ */
+public interface OnDismissCallback {
+
+ /**
+ * Called when the user has indicated they she would like to dismiss one or
+ * more list item positions. When this method is called given positions should be
+ * removed from the adapter.
+ *
+ * @param listView The originating ListView implementation
+ * @param reverseSortedPositions An array of positions to dismiss, sorted in descending order
+ * for convenience.
+ */
+ void onDismiss(@NonNull ViewGroup listView, @NonNull int[] reverseSortedPositions);
+}
\ No newline at end of file
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java
new file mode 100644
index 00000000..445a380e
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.ArrayAdapter;
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+
+/**
+ * Adds an option to swipe items in an {@link android.widget.AbsListView} away.
+ * Do not call {@link android.widget.AbsListView#setOnTouchListener(android.view.View.OnTouchListener)} on your {@code AbsListView}!
+ */
+public class SwipeDismissAdapter extends BaseAdapterDecorator {
+
+ @NonNull
+ private final OnDismissCallback mOnDismissCallback;
+
+ @Nullable
+ private SwipeDismissTouchListener mDismissTouchListener;
+
+ /**
+ * A boolean to indicate whether the {@link android.widget.AbsListView} is in a horizontal scroll container.
+ */
+ private boolean mParentIsHorizontalScrollContainer;
+
+ /**
+ * The resource id of the child that can be used to swipe a view away.
+ */
+ private int mSwipeTouchChildResId;
+
+ /**
+ * Create a new SwipeDismissAdapter.
+ *
+ * @param baseAdapter the {@link android.widget.BaseAdapter to use}
+ * @param onDismissCallback the {@link OnDismissCallback} to be notified of dismissed items.
+ */
+ public SwipeDismissAdapter(@NonNull final BaseAdapter baseAdapter, @NonNull final OnDismissCallback onDismissCallback) {
+ super(baseAdapter);
+ mOnDismissCallback = onDismissCallback;
+ }
+
+ @Override
+ public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) {
+ super.setListViewWrapper(listViewWrapper);
+ if (getDecoratedBaseAdapter() instanceof ArrayAdapter>) {
+ ((ArrayAdapter>) getDecoratedBaseAdapter()).propagateNotifyDataSetChanged(this);
+ }
+ mDismissTouchListener = new SwipeDismissTouchListener(listViewWrapper, mOnDismissCallback);
+ if (mParentIsHorizontalScrollContainer) {
+ mDismissTouchListener.setParentIsHorizontalScrollContainer();
+ }
+ if (mSwipeTouchChildResId != 0) {
+ mDismissTouchListener.setTouchChild(mSwipeTouchChildResId);
+ }
+ listViewWrapper.getListView().setOnTouchListener(mDismissTouchListener);
+ }
+
+ /**
+ * If the adapter's {@link android.widget.AbsListView} is hosted inside a parent(/grand-parent/etc) that can scroll horizontally, horizontal swipes won't
+ * work, because the parent will prevent touch-events from reaching the {@code AbsListView}.
+ *
+ * Call this method to fix this behavior.
+ * Note that this will prevent the parent from scrolling horizontally when the user touches anywhere in a list item.
+ */
+ public void setParentIsHorizontalScrollContainer() {
+ mParentIsHorizontalScrollContainer = true;
+ mSwipeTouchChildResId = 0;
+ if (mDismissTouchListener != null) {
+ mDismissTouchListener.setParentIsHorizontalScrollContainer();
+ }
+ }
+
+ /**
+ * If the adapter's {@link android.widget.AbsListView} is hosted inside a parent(/grand-parent/etc) that can scroll horizontally, horizontal swipes won't
+ * work, because the parent will prevent touch events from reaching the {@code AbsListView}.
+ *
+ * If a {@code AbsListView} view has a child with the given resource id, the user can still swipe the list item by touching that child.
+ * If the user touches an area outside that child (but inside the list item view), then the swipe will not happen and the parent
+ * will do its job instead (scrolling horizontally).
+ *
+ * @param childResId The resource id of the list items' child that the user should touch to be able to swipe the list items.
+ */
+ public void setSwipeTouchChildResId(final int childResId) {
+ mSwipeTouchChildResId = childResId;
+ if (mDismissTouchListener != null) {
+ mDismissTouchListener.setTouchChild(childResId);
+ }
+ }
+
+ /**
+ * Dismisses the {@link android.view.View} corresponding to given position.
+ * Calling this method has the same effect as manually swiping an item off the screen.
+ *
+ * @param position the position of the item in the {@link android.widget.ListAdapter}.
+ */
+ public void dismiss(final int position) {
+ if (mDismissTouchListener == null) {
+ throw new IllegalStateException("Call setListViewWrapper on this SwipeDismissAdapter!");
+ }
+ mDismissTouchListener.dismiss(position);
+ }
+
+ /**
+ * Returns the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissTouchListener} that is used by this {@code SwipeDismissAdapter}.
+ *
+ * @return null if {@link #setListViewWrapper} has not been called yet.
+ */
+ @Nullable
+ public SwipeDismissTouchListener getDismissTouchListener() {
+ return mDismissTouchListener;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ if (mDismissTouchListener != null) {
+ mDismissTouchListener.notifyDataSetChanged();
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListener.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListener.java
new file mode 100644
index 00000000..cd39ba99
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissTouchListener.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.nhaarman.listviewanimations.util.AdapterViewUtil;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.animation.ValueAnimator;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeTouchListener} that directly dismisses the items when swiped.
+ */
+public class SwipeDismissTouchListener extends SwipeTouchListener {
+
+ /**
+ * The callback which gets notified of dismissed items.
+ */
+ @NonNull
+ private final OnDismissCallback mCallback;
+
+ /**
+ * The duration of the dismiss animation
+ */
+ private final long mDismissAnimationTime;
+
+ /**
+ * The {@link android.view.View}s that have been dismissed.
+ */
+ @NonNull
+ private final Collection mDismissedViews = new LinkedList<>();
+
+ /**
+ * The dismissed positions.
+ */
+ @NonNull
+ private final List mDismissedPositions = new LinkedList<>();
+
+ /**
+ * The number of active dismiss animations.
+ */
+ private int mActiveDismissCount;
+
+ /**
+ * A handler for posting {@link Runnable}s.
+ */
+ @NonNull
+ private final Handler mHandler = new Handler();
+
+ /**
+ * Constructs a new {@code SwipeDismissTouchListener} for the given {@link android.widget.AbsListView}.
+ *
+ * @param listViewWrapper The {@code ListViewWrapper} containing the ListView whose items should be dismissable.
+ * @param callback The callback to trigger when the user has indicated that he
+ */
+ @SuppressWarnings("UnnecessaryFullyQualifiedName")
+ public SwipeDismissTouchListener(@NonNull final ListViewWrapper listViewWrapper, @NonNull final OnDismissCallback callback) {
+ super(listViewWrapper);
+ mCallback = callback;
+ mDismissAnimationTime = listViewWrapper.getListView().getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
+ }
+
+ /**
+ * Dismisses the {@link android.view.View} corresponding to given position.
+ * Calling this method has the same effect as manually swiping an item off the screen.
+ *
+ * @param position the position of the item in the {@link android.widget.ListAdapter}. Must be visible.
+ */
+ public void dismiss(final int position) {
+ fling(position);
+ }
+
+ @Override
+ public void fling(final int position) {
+ int firstVisiblePosition = getListViewWrapper().getFirstVisiblePosition();
+ int lastVisiblePosition = getListViewWrapper().getLastVisiblePosition();
+
+ if (firstVisiblePosition <= position && position <= lastVisiblePosition) {
+ super.fling(position);
+ } else if (position > lastVisiblePosition) {
+ directDismiss(position);
+ } else {
+ dismissAbove(position);
+ }
+ }
+
+ protected void directDismiss(final int position) {
+ mDismissedPositions.add(position);
+ finalizeDismiss();
+ }
+
+ private void dismissAbove(final int position) {
+ View view = AdapterViewUtil.getViewForPosition(getListViewWrapper(), getListViewWrapper().getFirstVisiblePosition());
+
+ if (view != null) {
+ view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+ int scrollDistance = view.getMeasuredHeight();
+
+ getListViewWrapper().smoothScrollBy(scrollDistance, (int) mDismissAnimationTime);
+ mHandler.postDelayed(new RestoreScrollRunnable(scrollDistance, position), mDismissAnimationTime);
+ }
+ }
+
+ @Override
+ protected void afterCancelSwipe(@NonNull final View view, final int position) {
+ finalizeDismiss();
+ }
+
+ @Override
+ protected void afterViewFling(@NonNull final View view, final int position) {
+ performDismiss(view, position);
+ }
+
+ /**
+ * Animates the dismissed list item to zero-height and fires the dismiss callback when all dismissed list item animations have completed.
+ *
+ * @param view the dismissed {@link android.view.View}.
+ */
+ protected void performDismiss(@NonNull final View view, final int position) {
+ mDismissedViews.add(view);
+ mDismissedPositions.add(position);
+
+ ValueAnimator animator = ValueAnimator.ofInt(view.getHeight(), 1).setDuration(mDismissAnimationTime);
+ animator.addUpdateListener(new DismissAnimatorUpdateListener(view));
+ animator.addListener(new DismissAnimatorListener());
+ animator.start();
+
+ mActiveDismissCount++;
+ }
+
+ /**
+ * If necessary, notifies the {@link OnDismissCallback} to remove dismissed object from the adapter,
+ * and restores the {@link android.view.View} presentations.
+ */
+ protected void finalizeDismiss() {
+ if (mActiveDismissCount == 0 && getActiveSwipeCount() == 0) {
+ restoreViewPresentations(mDismissedViews);
+ notifyCallback(mDismissedPositions);
+
+ mDismissedViews.clear();
+ mDismissedPositions.clear();
+ }
+ }
+
+ /**
+ * Notifies the {@link OnDismissCallback} of dismissed items.
+ *
+ * @param dismissedPositions the positions that have been dismissed.
+ */
+ protected void notifyCallback(@NonNull final List dismissedPositions) {
+ if (!dismissedPositions.isEmpty()) {
+ Collections.sort(dismissedPositions, Collections.reverseOrder());
+
+ int[] dismissPositions = new int[dismissedPositions.size()];
+ int i = 0;
+ for (Integer dismissedPosition : dismissedPositions) {
+ dismissPositions[i] = dismissedPosition;
+ i++;
+ }
+ mCallback.onDismiss(getListViewWrapper().getListView(), dismissPositions);
+ }
+ }
+
+ /**
+ * Restores the presentation of given {@link android.view.View}s by calling {@link #restoreViewPresentation(android.view.View)}.
+ */
+ protected void restoreViewPresentations(@NonNull final Iterable views) {
+ for (View view : views) {
+ restoreViewPresentation(view);
+ }
+ }
+
+ @Override
+ protected void restoreViewPresentation(@NonNull final View view) {
+ super.restoreViewPresentation(view);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.height = 0;
+ view.setLayoutParams(layoutParams);
+ }
+
+ protected int getActiveDismissCount() {
+ return mActiveDismissCount;
+ }
+
+ public long getDismissAnimationTime() {
+ return mDismissAnimationTime;
+ }
+
+ /**
+ * An {@link com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener} which applies height animation to given {@link android.view.View}.
+ */
+ private static class DismissAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener {
+
+ @NonNull
+ private final View mView;
+
+ DismissAnimatorUpdateListener(@NonNull final View view) {
+ mView = view;
+ }
+
+ @Override
+ public void onAnimationUpdate(@NonNull final ValueAnimator animation) {
+ ViewGroup.LayoutParams layoutParams = mView.getLayoutParams();
+ layoutParams.height = (Integer) animation.getAnimatedValue();
+ mView.setLayoutParams(layoutParams);
+ }
+ }
+
+ private class DismissAnimatorListener extends AnimatorListenerAdapter {
+
+ @Override
+ public void onAnimationEnd(@NonNull final Animator animation) {
+ mActiveDismissCount--;
+ finalizeDismiss();
+ }
+ }
+
+ /**
+ * A {@link Runnable} which applies the dismiss of a position, and restores the scroll position.
+ */
+ private class RestoreScrollRunnable implements Runnable {
+
+ private final int mScrollDistance;
+ private final int mPosition;
+
+ /**
+ * Creates a new {@code RestoreScrollRunnable}.
+ *
+ * @param scrollDistance The scroll distance in pixels to restore.
+ * @param position the position to dismiss
+ */
+ RestoreScrollRunnable(final int scrollDistance, final int position) {
+ mScrollDistance = scrollDistance;
+ mPosition = position;
+ }
+
+ @Override
+ public void run() {
+ getListViewWrapper().smoothScrollBy(-mScrollDistance, 1);
+ directDismiss(mPosition);
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListener.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListener.java
new file mode 100644
index 00000000..81bee0b0
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeTouchListener.java
@@ -0,0 +1,710 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+/* Originally based on Roman Nurik's SwipeDismissListViewTouchListener (https://gist.github.com/romannurik/2980593). */
+package com.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
+
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+import com.nhaarman.listviewanimations.itemmanipulation.TouchEventHandler;
+import com.nhaarman.listviewanimations.util.AdapterViewUtil;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.animation.AnimatorSet;
+import com.nineoldandroids.animation.ObjectAnimator;
+import com.nineoldandroids.view.ViewHelper;
+
+/**
+ * An {@link android.view.View.OnTouchListener} that makes the list items in a {@link android.widget.AbsListView} swipeable.
+ * Implementations of this class should implement {@link #afterViewFling(android.view.View, int)} to specify what to do after an item has been swiped.
+ */
+public abstract class SwipeTouchListener implements View.OnTouchListener, TouchEventHandler {
+
+ /**
+ * TranslationX View property.
+ */
+ private static final String TRANSLATION_X = "translationX";
+
+ /**
+ * Alpha View property.
+ */
+ private static final String ALPHA = "alpha";
+
+ private static final int MIN_FLING_VELOCITY_FACTOR = 16;
+
+ /**
+ * The minimum distance in pixels that should be moved before starting horizontal item movement.
+ */
+ private final int mSlop;
+
+ /**
+ * The minimum velocity to initiate a fling, as measured in pixels per second.
+ */
+ private final int mMinFlingVelocity;
+
+ /**
+ * The maximum velocity to initiate a fling, as measured in pixels per second.
+ */
+ private final int mMaxFlingVelocity;
+
+ /**
+ * The duration of the fling animation.
+ */
+ private final long mAnimationTime;
+
+ @NonNull
+ private final ListViewWrapper mListViewWrapper;
+
+ /**
+ * The minimum alpha value of swiped Views.
+ */
+ private float mMinimumAlpha;
+
+ /**
+ * The width of the {@link android.widget.AbsListView} in pixels.
+ */
+ private int mViewWidth = 1;
+
+ /**
+ * The raw X coordinate of the down event.
+ */
+ private float mDownX;
+
+ /**
+ * The raw Y coordinate of the down event.
+ */
+ private float mDownY;
+
+ /**
+ * Indicates whether the user is swiping an item.
+ */
+ private boolean mSwiping;
+
+ /**
+ * The {@code VelocityTracker} used in the swipe movement.
+ */
+ @Nullable
+ private VelocityTracker mVelocityTracker;
+
+ /**
+ * The parent {@link android.view.View} being swiped.
+ */
+ @Nullable
+ private View mCurrentView;
+
+ /**
+ * The {@link android.view.View} that is actually being swiped.
+ */
+ @Nullable
+ private View mSwipingView;
+
+ /**
+ * The current position being swiped.
+ */
+ private int mCurrentPosition = AdapterView.INVALID_POSITION;
+
+ /**
+ * The number of items in the {@code AbsListView}, minus the pending dismissed items.
+ */
+ private int mVirtualListCount = -1;
+
+ /**
+ * Indicates whether the {@link android.widget.AbsListView} is in a horizontal scroll container.
+ * If so, this class will prevent the horizontal scroller from receiving any touch events.
+ */
+ private boolean mParentIsHorizontalScrollContainer;
+
+ /**
+ * The resource id of the {@link android.view.View} that may steal touch events from their parents. Useful for example
+ * when the {@link android.widget.AbsListView} is in a horizontal scroll container, but not the whole {@code AbsListView} should
+ * steal the touch events.
+ */
+ private int mTouchChildResId;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager} which decides
+ * whether or not a list item can be swiped.
+ */
+ @Nullable
+ private DismissableManager mDismissableManager;
+
+ /**
+ * The number of active swipe animations.
+ */
+ private int mActiveSwipeCount;
+
+ /**
+ * Indicates whether swipe is enabled.
+ */
+ private boolean mSwipeEnabled = true;
+
+ /**
+ * Constructs a new {@code SwipeTouchListener} for the given {@link android.widget.AbsListView}.
+ */
+ @SuppressWarnings("UnnecessaryFullyQualifiedName")
+ protected SwipeTouchListener(@NonNull final ListViewWrapper listViewWrapper) {
+ ViewConfiguration vc = ViewConfiguration.get(listViewWrapper.getListView().getContext());
+ mSlop = vc.getScaledTouchSlop();
+ mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * MIN_FLING_VELOCITY_FACTOR;
+ mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mAnimationTime = listViewWrapper.getListView().getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
+ mListViewWrapper = listViewWrapper;
+ }
+
+ /**
+ * Sets the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager} to specify which views can or cannot be swiped.
+ *
+ * @param dismissableManager {@code null} for no restrictions.
+ */
+ public void setDismissableManager(@Nullable final DismissableManager dismissableManager) {
+ mDismissableManager = dismissableManager;
+ }
+
+ /**
+ * Set the minimum value of the alpha property swiping Views should have.
+ *
+ * @param minimumAlpha the alpha value between 0.0f and 1.0f.
+ */
+ public void setMinimumAlpha(final float minimumAlpha) {
+ mMinimumAlpha = minimumAlpha;
+ }
+
+ /**
+ * If the {@link android.widget.AbsListView} is hosted inside a parent(/grand-parent/etc) that can scroll horizontally, horizontal swipes won't
+ * work, because the parent will prevent touch-events from reaching the {@code AbsListView}.
+ *
+ * Call this method to fix this behavior.
+ * Note that this will prevent the parent from scrolling horizontally when the user touches anywhere in a list item.
+ */
+ public void setParentIsHorizontalScrollContainer() {
+ mParentIsHorizontalScrollContainer = true;
+ mTouchChildResId = 0;
+ }
+
+ /**
+ * Sets the resource id of a child view that should be touched to engage swipe.
+ * When the user touches a region outside of that view, no swiping will occur.
+ *
+ * @param childResId The resource id of the list items' child that the user should touch to be able to swipe the list items.
+ */
+ public void setTouchChild(final int childResId) {
+ mTouchChildResId = childResId;
+ mParentIsHorizontalScrollContainer = false;
+ }
+
+ /**
+ * Notifies this {@code SwipeTouchListener} that the adapter contents have changed.
+ */
+ public void notifyDataSetChanged() {
+ if (mListViewWrapper.getAdapter() != null) {
+ mVirtualListCount = mListViewWrapper.getAdapter().getCount();
+ }
+ }
+
+ /**
+ * Returns whether the user is currently swiping an item.
+ *
+ * @return {@code true} if the user is swiping an item.
+ */
+ public boolean isSwiping() {
+ return mSwiping;
+ }
+
+ @NonNull
+ public ListViewWrapper getListViewWrapper() {
+ return mListViewWrapper;
+ }
+
+ /**
+ * Enables the swipe behavior.
+ */
+ public void enableSwipe() {
+ mSwipeEnabled = true;
+ }
+
+ /**
+ * Disables the swipe behavior.
+ */
+ public void disableSwipe() {
+ mSwipeEnabled = false;
+ }
+
+ /**
+ * Flings the {@link android.view.View} corresponding to given position out of sight.
+ * Calling this method has the same effect as manually swiping an item off the screen.
+ *
+ * @param position the position of the item in the {@link android.widget.ListAdapter}. Must be visible.
+ */
+ public void fling(final int position) {
+ int firstVisiblePosition = mListViewWrapper.getFirstVisiblePosition();
+ int lastVisiblePosition = mListViewWrapper.getLastVisiblePosition();
+ if (position < firstVisiblePosition || position > lastVisiblePosition) {
+ throw new IllegalArgumentException("View for position " + position + " not visible!");
+ }
+
+ View downView = AdapterViewUtil.getViewForPosition(mListViewWrapper, position);
+ if (downView == null) {
+ throw new IllegalStateException("No view found for position " + position);
+ }
+ flingView(downView, position, true);
+
+ mActiveSwipeCount++;
+ mVirtualListCount--;
+ }
+
+ @Override
+ public boolean isInteracting() {
+ return mSwiping;
+ }
+
+ @Override
+ public boolean onTouchEvent(@NonNull final MotionEvent event) {
+ return onTouch(null, event);
+ }
+
+ @Override
+ public boolean onTouch(@Nullable final View view, @NonNull final MotionEvent event) {
+ if (mListViewWrapper.getAdapter() == null) {
+ return false;
+ }
+
+ if (mVirtualListCount == -1) {
+ mVirtualListCount = mListViewWrapper.getAdapter().getCount();
+ }
+
+ if (mViewWidth < 2) {
+ mViewWidth = mListViewWrapper.getListView().getWidth();
+ }
+
+ boolean result;
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ result = handleDownEvent(view, event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ result = handleMoveEvent(view, event);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ result = handleCancelEvent();
+ break;
+ case MotionEvent.ACTION_UP:
+ result = handleUpEvent(event);
+ break;
+ default:
+ result = false;
+ }
+
+ return result;
+ }
+
+ private boolean handleDownEvent(@Nullable final View view, @NonNull final MotionEvent motionEvent) {
+ if (!mSwipeEnabled) {
+ return false;
+ }
+
+ View downView = findDownView(motionEvent);
+ if (downView == null) {
+ return false;
+ }
+
+ int downPosition = AdapterViewUtil.getPositionForView(mListViewWrapper, downView);
+ if (!isDismissable(downPosition)) {
+ return false;
+ }
+
+ /* Check if we are processing the item at this position */
+ if (mCurrentPosition == downPosition || downPosition >= mVirtualListCount) {
+ return false;
+ }
+
+ if (view != null) {
+ view.onTouchEvent(motionEvent);
+ }
+
+ disableHorizontalScrollContainerIfNecessary(motionEvent, downView);
+
+ mDownX = motionEvent.getRawX();
+ mDownY = motionEvent.getRawY();
+
+ mCurrentView = downView;
+ mSwipingView = getSwipeView(downView);
+ mCurrentPosition = downPosition;
+
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(motionEvent);
+ return true;
+ }
+
+ /**
+ * Returns the child {@link android.view.View} that was touched, by performing a hit test.
+ *
+ * @param motionEvent the {@link android.view.MotionEvent} to find the {@code View} for.
+ *
+ * @return the touched {@code View}, or {@code null} if none found.
+ */
+ @Nullable
+ private View findDownView(@NonNull final MotionEvent motionEvent) {
+ Rect rect = new Rect();
+ int childCount = mListViewWrapper.getChildCount();
+ int[] listViewCoords = new int[2];
+ mListViewWrapper.getListView().getLocationOnScreen(listViewCoords);
+ int x = (int) motionEvent.getRawX() - listViewCoords[0];
+ int y = (int) motionEvent.getRawY() - listViewCoords[1];
+ View downView = null;
+ for (int i = 0; i < childCount && downView == null; i++) {
+ View child = mListViewWrapper.getChildAt(i);
+ if (child != null) {
+ child.getHitRect(rect);
+ if (rect.contains(x, y)) {
+ downView = child;
+ }
+ }
+ }
+ return downView;
+ }
+
+ /**
+ * Finds out whether the item represented by given position is dismissable.
+ *
+ * @param position the position of the item.
+ *
+ * @return {@code true} if the item is dismissable, false otherwise.
+ */
+ private boolean isDismissable(final int position) {
+ if (mListViewWrapper.getAdapter() == null) {
+ return false;
+ }
+
+ if (mDismissableManager != null) {
+ long downId = mListViewWrapper.getAdapter().getItemId(position);
+ return mDismissableManager.isDismissable(downId, position);
+ }
+ return true;
+ }
+
+ private void disableHorizontalScrollContainerIfNecessary(@NonNull final MotionEvent motionEvent, @NonNull final View view) {
+ if (mParentIsHorizontalScrollContainer) {
+ mListViewWrapper.getListView().requestDisallowInterceptTouchEvent(true);
+ } else if (mTouchChildResId != 0) {
+ mParentIsHorizontalScrollContainer = false;
+
+ final View childView = view.findViewById(mTouchChildResId);
+ if (childView != null) {
+ final Rect childRect = getChildViewRect(mListViewWrapper.getListView(), childView);
+ if (childRect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) {
+ mListViewWrapper.getListView().requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ }
+ }
+
+ private boolean handleMoveEvent(@Nullable final View view, @NonNull final MotionEvent motionEvent) {
+ if (mVelocityTracker == null || mCurrentView == null) {
+ return false;
+ }
+
+ mVelocityTracker.addMovement(motionEvent);
+
+ float deltaX = motionEvent.getRawX() - mDownX;
+ float deltaY = motionEvent.getRawY() - mDownY;
+
+ if (Math.abs(deltaX) > mSlop && Math.abs(deltaX) > Math.abs(deltaY)) {
+ if (!mSwiping) {
+ mActiveSwipeCount++;
+ onStartSwipe(mCurrentView, mCurrentPosition);
+ }
+ mSwiping = true;
+ mListViewWrapper.getListView().requestDisallowInterceptTouchEvent(true);
+
+ /* Cancel ListView's touch (un-highlighting the item) */
+ if (view != null) {
+ MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
+ cancelEvent.setAction(MotionEvent.ACTION_CANCEL | motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+ view.onTouchEvent(cancelEvent);
+ cancelEvent.recycle();
+ }
+ }
+
+ if (mSwiping) {
+ ViewHelper.setTranslationX(mSwipingView, deltaX);
+ ViewHelper.setAlpha(mSwipingView, Math.max(mMinimumAlpha, Math.min(1, 1 - 2 * Math.abs(deltaX) / mViewWidth)));
+ return true;
+ }
+ return false;
+ }
+
+ private boolean handleCancelEvent() {
+ if (mVelocityTracker == null || mCurrentView == null) {
+ return false;
+ }
+
+ if (mCurrentPosition != AdapterView.INVALID_POSITION && mSwiping) {
+ onCancelSwipe(mCurrentView, mCurrentPosition);
+ restoreCurrentViewTranslation();
+ }
+
+ reset();
+ return false;
+ }
+
+ private boolean handleUpEvent(@NonNull final MotionEvent motionEvent) {
+ if (mVelocityTracker == null || mCurrentView == null) {
+ return false;
+ }
+
+ if (mSwiping) {
+ float deltaX = motionEvent.getRawX() - mDownX;
+
+ mVelocityTracker.addMovement(motionEvent);
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ float velocityX = Math.abs(mVelocityTracker.getXVelocity());
+ float velocityY = Math.abs(mVelocityTracker.getYVelocity());
+
+ boolean shouldDismiss = false;
+ boolean dismissToRight = false;
+
+ if (Math.abs(deltaX) > mViewWidth / 2) {
+ shouldDismiss = true;
+ dismissToRight = deltaX > 0;
+ } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
+ shouldDismiss = true;
+ dismissToRight = mVelocityTracker.getXVelocity() > 0;
+ }
+
+
+ if (shouldDismiss) {
+ beforeViewFling(mCurrentView, mCurrentPosition);
+ flingCurrentView(dismissToRight);
+ mVirtualListCount--;
+ } else {
+ onCancelSwipe(mCurrentView, mCurrentPosition);
+ restoreCurrentViewTranslation();
+ }
+ }
+
+ reset();
+ return false;
+ }
+
+ /**
+ * Flings the pending {@link android.view.View} out of sight.
+ *
+ * @param flingToRight {@code true} if the {@code View} should be flinged to the right, {@code false} if it should be flinged to the left.
+ */
+ private void flingCurrentView(final boolean flingToRight) {
+ if (mCurrentView != null) {
+ flingView(mCurrentView, mCurrentPosition, flingToRight);
+ }
+ }
+
+ /**
+ * Flings given {@link android.view.View} out of sight.
+ *
+ * @param view the parent {@link android.view.View}.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ * @param flingToRight {@code true} {@code true} if the {@code View} should be flinged to the right, {@code false} if it should be flinged to the left.
+ */
+ private void flingView(@NonNull final View view, final int position, final boolean flingToRight) {
+ if (mViewWidth < 2) {
+ mViewWidth = mListViewWrapper.getListView().getWidth();
+ }
+
+ View swipeView = getSwipeView(view);
+ ObjectAnimator xAnimator = ObjectAnimator.ofFloat(swipeView, TRANSLATION_X, flingToRight ? mViewWidth : -mViewWidth);
+ ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(swipeView, ALPHA, 0);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(xAnimator, alphaAnimator);
+ animatorSet.setDuration(mAnimationTime);
+ animatorSet.addListener(new FlingAnimatorListener(view, position));
+ animatorSet.start();
+ }
+
+ /**
+ * Animates the pending {@link android.view.View} back to its original position.
+ */
+ private void restoreCurrentViewTranslation() {
+ if (mCurrentView == null) {
+ return;
+ }
+
+ ObjectAnimator xAnimator = ObjectAnimator.ofFloat(mSwipingView, TRANSLATION_X, 0);
+ ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mSwipingView, ALPHA, 1);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(xAnimator, alphaAnimator);
+ animatorSet.setDuration(mAnimationTime);
+ animatorSet.addListener(new RestoreAnimatorListener(mCurrentView, mCurrentPosition));
+ animatorSet.start();
+ }
+
+ /**
+ * Resets the fields to the initial values, ready to start over.
+ */
+ private void reset() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ }
+
+ mVelocityTracker = null;
+ mDownX = 0;
+ mDownY = 0;
+ mCurrentView = null;
+ mSwipingView = null;
+ mCurrentPosition = AdapterView.INVALID_POSITION;
+ mSwiping = false;
+ }
+
+ /**
+ * Called when the user starts swiping a {@link android.view.View}.
+ *
+ * @param view the {@code View} that is being swiped.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ */
+ protected void onStartSwipe(@NonNull final View view, final int position) {
+ }
+
+ /**
+ * Called when the swipe movement is canceled. A restore animation starts at this point.
+ *
+ * @param view the {@code View} that was swiped.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ */
+ protected void onCancelSwipe(@NonNull final View view, final int position) {
+ }
+
+ /**
+ * Called after the restore animation of a canceled swipe movement ends.
+ *
+ * @param view the {@code View} that is being swiped.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ */
+ protected void afterCancelSwipe(@NonNull final View view, final int position) {
+ }
+
+ /**
+ * Called when the user lifted their finger off the screen, and the {@link android.view.View} should be swiped away. A fling animation starts at this point.
+ *
+ * @param view the {@code View} that is being flinged.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ */
+ protected void beforeViewFling(@NonNull final View view, final int position) {
+ }
+
+ /**
+ * Called after the fling animation of a succesful swipe ends.
+ * Users of this class should implement any finalizing behavior at this point, such as notifying the adapter.
+ *
+ * @param view the {@code View} that is being swiped.
+ * @param position the position of the item in the {@link android.widget.ListAdapter} corresponding to the {@code View}.
+ */
+ protected abstract void afterViewFling(@NonNull View view, int position);
+
+ /**
+ * Restores the {@link android.view.View}'s {@code alpha} and {@code translationX} values.
+ * Users of this class should call this method when recycling {@code View}s.
+ *
+ * @param view the {@code View} whose presentation should be restored.
+ */
+ protected void restoreViewPresentation(@NonNull final View view) {
+ View swipedView = getSwipeView(view);
+ ViewHelper.setAlpha(swipedView, 1);
+ ViewHelper.setTranslationX(swipedView, 0);
+ }
+
+ /**
+ * Returns the number of active swipe animations.
+ */
+ protected int getActiveSwipeCount() {
+ return mActiveSwipeCount;
+ }
+
+ /**
+ * Returns the {@link android.view.View} that should be swiped away. Must be a child of given {@code View}, or the {@code View} itself.
+ *
+ * @param view the parent {@link android.view.View}.
+ */
+ @NonNull
+ protected View getSwipeView(@NonNull final View view) {
+ return view;
+ }
+
+ private static Rect getChildViewRect(final View parentView, final View childView) {
+ Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
+ if (!parentView.equals(childView)) {
+ View workingChildView = childView;
+ ViewGroup parent;
+ while (!(parent = (ViewGroup) workingChildView.getParent()).equals(parentView)) {
+ childRect.offset(parent.getLeft(), parent.getTop());
+ workingChildView = parent;
+ }
+ }
+ return childRect;
+ }
+
+ /**
+ * An {@link com.nineoldandroids.animation.Animator.AnimatorListener} that notifies when the fling animation has ended.
+ */
+ private class FlingAnimatorListener extends AnimatorListenerAdapter {
+
+ @NonNull
+ private final View mView;
+
+ private final int mPosition;
+
+ private FlingAnimatorListener(@NonNull final View view, final int position) {
+ mView = view;
+ mPosition = position;
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull final Animator animation) {
+ mActiveSwipeCount--;
+ afterViewFling(mView, mPosition);
+ }
+ }
+
+ /**
+ * An {@link com.nineoldandroids.animation.Animator.AnimatorListener} that performs the dismissal animation when the current animation has ended.
+ */
+ private class RestoreAnimatorListener extends AnimatorListenerAdapter {
+
+ @NonNull
+ private final View mView;
+
+ private final int mPosition;
+
+ private RestoreAnimatorListener(@NonNull final View view, final int position) {
+ mView = view;
+ mPosition = position;
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull final Animator animation) {
+ mActiveSwipeCount--;
+ afterCancelSwipe(mView, mPosition);
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SimpleSwipeUndoAdapter.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SimpleSwipeUndoAdapter.java
new file mode 100644
index 00000000..836295db
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SimpleSwipeUndoAdapter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * An implementation of {@link SwipeUndoAdapter} which puts the primary and undo {@link android.view.View} in a {@link android.widget.FrameLayout},
+ * and handles the undo click event.
+ */
+public class SimpleSwipeUndoAdapter extends SwipeUndoAdapter implements UndoCallback {
+
+ @NonNull
+ private final Context mContext;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback} that is notified of dismissed items.
+ */
+ @NonNull
+ private final OnDismissCallback mOnDismissCallback;
+
+ /**
+ * The {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter} that provides the undo {@link android.view.View}s.
+ */
+ @NonNull
+ private final UndoAdapter mUndoAdapter;
+
+ /**
+ * The positions of the items currently in the undo state.
+ */
+ private final Collection mUndoPositions = new ArrayList<>();
+
+ /**
+ * Create a new {@code SimpleSwipeUndoAdapterGen}, decorating given {@link android.widget.BaseAdapter}.
+ *
+ * @param undoAdapter the {@link android.widget.BaseAdapter} that is decorated. Must implement
+ * {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter}.
+ * @param context the {@link android.content.Context}.
+ * @param dismissCallback the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback} that is notified of dismissed items.
+ */
+ public SimpleSwipeUndoAdapter(@NonNull final BaseAdapter adapter, @NonNull final Context context,
+ @NonNull final OnDismissCallback dismissCallback) {
+ // We fix this right away
+ // noinspection ConstantConditions
+ super(adapter, null);
+ setUndoCallback(this);
+
+ BaseAdapter undoAdapter = adapter;
+ while (undoAdapter instanceof BaseAdapterDecorator) {
+ undoAdapter = ((BaseAdapterDecorator) undoAdapter).getDecoratedBaseAdapter();
+ }
+
+ if (!(undoAdapter instanceof UndoAdapter)) {
+ throw new IllegalStateException("BaseAdapter must implement UndoAdapter!");
+ }
+
+ mUndoAdapter = (UndoAdapter) undoAdapter;
+ mContext = context;
+ mOnDismissCallback = dismissCallback;
+ }
+
+ @NonNull
+ @Override
+ public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ SwipeUndoView view = (SwipeUndoView) convertView;
+ if (view == null) {
+ view = new SwipeUndoView(mContext);
+ }
+ View primaryView = super.getView(position, view.getPrimaryView(), view);
+ view.setPrimaryView(primaryView);
+
+ View undoView = mUndoAdapter.getUndoView(position, view.getUndoView(), view);
+ view.setUndoView(undoView);
+
+ mUndoAdapter.getUndoClickView(undoView).setOnClickListener(new UndoClickListener(view, position));
+
+ boolean isInUndoState = mUndoPositions.contains(position);
+ primaryView.setVisibility(isInUndoState ? View.GONE : View.VISIBLE);
+ undoView.setVisibility(isInUndoState ? View.VISIBLE : View.GONE);
+
+ return view;
+ }
+
+ @Override
+ @NonNull
+ public View getPrimaryView(@NonNull final View view) {
+ View primaryView = ((SwipeUndoView) view).getPrimaryView();
+ if (primaryView == null) {
+ throw new IllegalStateException("primaryView == null");
+ }
+ return primaryView;
+ }
+
+ @Override
+ @NonNull
+ public View getUndoView(@NonNull final View view) {
+ View undoView = ((SwipeUndoView) view).getUndoView();
+ if (undoView == null) {
+ throw new IllegalStateException("undoView == null");
+ }
+ return undoView;
+ }
+
+ @Override
+ public void onUndoShown(@NonNull final View view, final int position) {
+ mUndoPositions.add(position);
+ }
+
+ @Override
+ public void onUndo(@NonNull final View view, final int position) {
+ mUndoPositions.remove(position);
+ }
+
+ @Override
+ public void onDismiss(@NonNull final View view, final int position) {
+ mUndoPositions.remove(position);
+ }
+
+ @Override
+ public void onDismiss(@NonNull final ViewGroup listView, @NonNull final int[] reverseSortedPositions) {
+ mOnDismissCallback.onDismiss(listView, reverseSortedPositions);
+
+ Collection newUndoPositions = Util.processDeletions(mUndoPositions, reverseSortedPositions);
+ mUndoPositions.clear();
+ mUndoPositions.addAll(newUndoPositions);
+ }
+
+
+ private class UndoClickListener implements View.OnClickListener {
+
+ @NonNull
+ private final SwipeUndoView mView;
+
+ private final int mPosition;
+
+ UndoClickListener(@NonNull final SwipeUndoView view, final int position) {
+ mView = view;
+ mPosition = position;
+ }
+
+ @Override
+ public void onClick(@NonNull final View v) {
+ undo(mView);
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoAdapter.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoAdapter.java
new file mode 100644
index 00000000..5c11b9d1
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.BaseAdapterDecorator;
+import com.nhaarman.listviewanimations.itemmanipulation.DynamicListView;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+
+/**
+ * Adds swipe-undo behaviour to the {@link android.widget.AbsListView}, using a {@link SwipeUndoTouchListener}.
+ */
+public abstract class SwipeUndoAdapter extends BaseAdapterDecorator {
+
+ /**
+ * The {@link SwipeUndoTouchListener} that is set to the {@link android.widget.AbsListView}.
+ */
+ @NonNull
+ private SwipeUndoTouchListener mSwipeUndoTouchListener;
+
+ /**
+ * The {@link UndoCallback} that is used.
+ */
+ @NonNull
+ private UndoCallback mUndoCallback;
+
+ /**
+ * Create a new {@code SwipeUndoAdapter}, decorating given {@link android.widget.BaseAdapter}.
+ *
+ * @param baseAdapter the {@link android.widget.BaseAdapter} to decorate.
+ * @param undoCallback the {@link UndoCallback} that is used.
+ */
+ protected SwipeUndoAdapter(@NonNull final BaseAdapter baseAdapter, @NonNull final UndoCallback undoCallback) {
+ super(baseAdapter);
+ mUndoCallback = undoCallback;
+ }
+
+ @Override
+ public void setListViewWrapper(@NonNull final ListViewWrapper listViewWrapper) {
+ super.setListViewWrapper(listViewWrapper);
+ mSwipeUndoTouchListener = new SwipeUndoTouchListener(listViewWrapper, mUndoCallback);
+
+ if (!(listViewWrapper.getListView() instanceof DynamicListView)) {
+ listViewWrapper.getListView().setOnTouchListener(mSwipeUndoTouchListener);
+ }
+ }
+
+ public void setSwipeUndoTouchListener(@NonNull final SwipeUndoTouchListener swipeUndoTouchListener) {
+ mSwipeUndoTouchListener = swipeUndoTouchListener;
+ }
+
+ @NonNull
+ @Override
+ public View getView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent) {
+ if (getListViewWrapper() == null) {
+ throw new IllegalArgumentException("Call setAbsListView() on this SwipeUndoAdapter before setAdapter()!");
+ }
+ return super.getView(position, convertView, parent);
+ }
+
+ /**
+ * Sets the {@link UndoCallback} to use.
+ */
+ public void setUndoCallback(@NonNull final UndoCallback undoCallback) {
+ mUndoCallback = undoCallback;
+ }
+
+ @NonNull
+ public UndoCallback getUndoCallback() {
+ return mUndoCallback;
+ }
+
+ /**
+ * Performs the undo animation and restores the original state for given {@link android.view.View}.
+ *
+ * @param view the parent {@code View} which contains both primary and undo {@code View}s.
+ */
+ public void undo(@NonNull final View view) {
+ mSwipeUndoTouchListener.undo(view);
+ }
+
+ /**
+ * Dismisses the {@link android.view.View} corresponding to given position. Calling this method has the same effect as manually swiping an item off the screen.
+ *
+ * @param position the position of the item in the {@link android.widget.ListAdapter}. Must be visible.
+ */
+ public void dismiss(final int position) {
+ mSwipeUndoTouchListener.dismiss(position);
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListener.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListener.java
new file mode 100644
index 00000000..f787fc40
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoTouchListener.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissTouchListener;
+import com.nhaarman.listviewanimations.util.AdapterViewUtil;
+import com.nhaarman.listviewanimations.util.ListViewWrapper;
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.animation.AnimatorSet;
+import com.nineoldandroids.animation.ObjectAnimator;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissTouchListener} that adds an undo stage to the item swiping.
+ */
+public class SwipeUndoTouchListener extends SwipeDismissTouchListener {
+
+ private static final String ALPHA = "alpha";
+
+ private static final String TRANSLATION_X = "translationX";
+
+ /**
+ * The callback which gets notified of events.
+ */
+ @NonNull
+ private final UndoCallback mCallback;
+
+ /**
+ * The positions that are in the undo state.
+ */
+ @NonNull
+ private final Collection mUndoPositions = new LinkedList<>();
+
+ /**
+ * The {@link android.view.View}s that are in the undo state.
+ */
+ @NonNull
+ private final Map mUndoViews = new HashMap<>();
+
+ /**
+ * The positions that have been dismissed.
+ */
+ @NonNull
+ private final List mDismissedPositions = new LinkedList<>();
+
+ /**
+ * The {@link android.view.View}s that have been dismissed.
+ */
+ @NonNull
+ private final Collection mDismissedViews = new LinkedList<>();
+
+ public SwipeUndoTouchListener(@NonNull final ListViewWrapper listViewWrapper, @NonNull final UndoCallback callback) {
+ super(listViewWrapper, callback);
+ mCallback = callback;
+ }
+
+ @Override
+ protected void afterViewFling(@NonNull final View view, final int position) {
+ if (mUndoPositions.contains(position)) {
+ mUndoPositions.remove(position);
+ mUndoViews.remove(position);
+ performDismiss(view, position);
+ hideUndoView(view);
+ } else {
+ mUndoPositions.add(position);
+ mUndoViews.put(position, view);
+ mCallback.onUndoShown(view, position);
+ showUndoView(view);
+ restoreViewPresentation(view);
+ }
+ }
+
+ @Override
+ protected void afterCancelSwipe(@NonNull final View view, final int position) {
+ finalizeDismiss();
+ }
+
+ /**
+ * Animates the dismissed list item to zero-height and fires the dismiss callback when all dismissed list item animations have completed.
+ *
+ * @param view the dismissed {@link android.view.View}.
+ */
+ @Override
+ protected void performDismiss(@NonNull final View view, final int position) {
+ super.performDismiss(view, position);
+
+ mDismissedViews.add(view);
+ mDismissedPositions.add(position);
+
+ mCallback.onDismiss(view, position);
+ }
+
+ public boolean hasPendingItems() {
+ return !mUndoPositions.isEmpty();
+ }
+
+ /**
+ * Dismisses all items that are in the undo state.
+ */
+ public void dimissPending() {
+ for (int position : mUndoPositions) {
+ performDismiss(mUndoViews.get(position), position);
+ }
+ }
+
+ /**
+ * Sets the visibility of the primary {@link android.view.View} to {@link android.view.View#GONE}, and animates the undo {@code View} in to view.
+ *
+ * @param view the parent {@code View} which contains both primary and undo {@code View}s.
+ */
+ private void showUndoView(@NonNull final View view) {
+ mCallback.getPrimaryView(view).setVisibility(View.GONE);
+
+ View undoView = mCallback.getUndoView(view);
+ undoView.setVisibility(View.VISIBLE);
+ ObjectAnimator.ofFloat(undoView, ALPHA, 0f, 1f).start();
+ }
+
+ /**
+ * Sets the visibility of the primary {@link android.view.View} to {@link android.view.View#VISIBLE}, and that of the undo {@code View} to {@link android.view.View#GONE}.
+ *
+ * @param view the parent {@code View} which contains both primary and undo {@code View}s.
+ */
+ private void hideUndoView(@NonNull final View view) {
+ mCallback.getPrimaryView(view).setVisibility(View.VISIBLE);
+ mCallback.getUndoView(view).setVisibility(View.GONE);
+ }
+
+
+ /**
+ * If necessary, notifies the {@link UndoCallback} to remove dismissed object from the adapter,
+ * and restores the {@link android.view.View} presentations.
+ */
+ @Override
+ protected void finalizeDismiss() {
+ if (getActiveDismissCount() == 0 && getActiveSwipeCount() == 0) {
+ restoreViewPresentations(mDismissedViews);
+ notifyCallback(mDismissedPositions);
+
+ Collection newUndoPositions = Util.processDeletions(mUndoPositions, mDismissedPositions);
+ mUndoPositions.clear();
+ mUndoPositions.addAll(newUndoPositions);
+
+ mDismissedViews.clear();
+ mDismissedPositions.clear();
+ }
+ }
+
+ /**
+ * Restores the height of given {@code View}.
+ * Also calls its super implementation.
+ */
+ @Override
+ protected void restoreViewPresentation(@NonNull final View view) {
+ super.restoreViewPresentation(view);
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ layoutParams.height = 0;
+ view.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * Performs the undo animation and restores the original state for given {@link android.view.View}.
+ *
+ * @param view the parent {@code View} which contains both primary and undo {@code View}s.
+ */
+ public void undo(@NonNull final View view) {
+ int position = AdapterViewUtil.getPositionForView(getListViewWrapper(), view);
+ mUndoPositions.remove(position);
+
+ View primaryView = mCallback.getPrimaryView(view);
+ View undoView = mCallback.getUndoView(view);
+
+ primaryView.setVisibility(View.VISIBLE);
+
+ ObjectAnimator undoAlphaAnimator = ObjectAnimator.ofFloat(undoView, ALPHA, 1f, 0f);
+ ObjectAnimator primaryAlphaAnimator = ObjectAnimator.ofFloat(primaryView, ALPHA, 0f, 1f);
+ ObjectAnimator primaryXAnimator = ObjectAnimator.ofFloat(primaryView, TRANSLATION_X, primaryView.getWidth(), 0f);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(undoAlphaAnimator, primaryAlphaAnimator, primaryXAnimator);
+ animatorSet.addListener(new UndoAnimatorListener(undoView));
+ animatorSet.start();
+
+ mCallback.onUndo(view, position);
+ }
+
+ @Override
+ protected void directDismiss(final int position) {
+ mDismissedPositions.add(position);
+ finalizeDismiss();
+ }
+
+ /**
+ * An {@link com.nineoldandroids.animation.Animator.AnimatorListener} which finalizes the undo when the animation is finished.
+ */
+ private class UndoAnimatorListener extends AnimatorListenerAdapter {
+
+ @NonNull
+ private final View mUndoView;
+
+ UndoAnimatorListener(@NonNull final View undoView) {
+ mUndoView = undoView;
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull final Animator animation) {
+ mUndoView.setVisibility(View.GONE);
+ finalizeDismiss();
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoView.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoView.java
new file mode 100644
index 00000000..aef72e30
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/SwipeUndoView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * A convenience class which holds a primary and a undo {@link android.view.View}.
+ */
+class SwipeUndoView extends FrameLayout {
+
+ /**
+ * The primary {@link android.view.View}.
+ */
+ @Nullable
+ private View mPrimaryView;
+
+ /**
+ * The undo {@link android.view.View}.
+ */
+ @Nullable
+ private View mUndoView;
+
+ /**
+ * Creates a new {@code SwipeUndoView}.
+ */
+ SwipeUndoView(final Context context) {
+ super(context);
+ }
+
+ /**
+ * Sets the primary {@link android.view.View}. Removes any existing primary {@code View} if present.
+ */
+ void setPrimaryView(@NonNull final View primaryView) {
+ if (mPrimaryView != null) {
+ removeView(mPrimaryView);
+ }
+ mPrimaryView = primaryView;
+ addView(mPrimaryView);
+ }
+
+ /**
+ * Sets the undo {@link android.view.View}. Removes any existing primary {@code View} if present, and sets the visibility of the {@code undoView} to {@link #GONE}.
+ */
+ void setUndoView(@NonNull final View undoView) {
+ if (mUndoView != null) {
+ removeView(mUndoView);
+ }
+ mUndoView = undoView;
+ mUndoView.setVisibility(GONE);
+ addView(mUndoView);
+ }
+
+ /**
+ * Returns the primary {@link android.view.View}.
+ */
+ @Nullable
+ View getPrimaryView() {
+ return mPrimaryView;
+ }
+
+ /**
+ * Returns the undo {@link android.view.View}.
+ */
+ @Nullable
+ View getUndoView() {
+ return mUndoView;
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/TimedUndoAdapter.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/TimedUndoAdapter.java
new file mode 100644
index 00000000..0cfb71da
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/TimedUndoAdapter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.content.Context;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link SimpleSwipeUndoAdapter} which automatically dismisses items after a timeout.
+ */
+public class TimedUndoAdapter extends SimpleSwipeUndoAdapter {
+
+ /**
+ * The default time in milliseconds before an item in the undo state should automatically dismiss.
+ */
+ public static final long DEFAULT_TIMEOUT_MS = 3000;
+
+ /**
+ * The time in milliseconds before an item in the undo state should automatically dismiss.
+ * Defaults to {@value #DEFAULT_TIMEOUT_MS}.
+ */
+ private long mTimeoutMs = DEFAULT_TIMEOUT_MS;
+
+ /**
+ * A {@link android.os.Handler} to post {@link TimeoutRunnable}s to.
+ */
+ @NonNull
+ private final Handler mHandler = new Handler();
+
+ /**
+ * The {@link TimeoutRunnable}s which are posted. Keys are positions.
+ */
+ //noinspection UseSparseArrays
+ private final Map mRunnables = new HashMap<>();
+
+ /**
+ * Creates a new {@code TimedUndoAdapterGen}, decorating given {@link android.widget.BaseAdapter}.
+ *
+ * @param undoAdapter the {@link android.widget.BaseAdapter} that is decorated. Must implement
+ * {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo.UndoAdapter}.
+ * @param context the {@link android.content.Context}.
+ * @param dismissCallback the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback} that is notified of dismissed items.
+ */
+ public TimedUndoAdapter(@NonNull final V undoAdapter, @NonNull final Context context, @NonNull final OnDismissCallback
+ dismissCallback) {
+ super(undoAdapter, context, dismissCallback);
+ }
+
+ /**
+ * Sets the time in milliseconds after which an item in the undo state should automatically dismiss.
+ * Defaults to {@value #DEFAULT_TIMEOUT_MS}.
+ */
+ public void setTimeoutMs(final long timeoutMs) {
+ mTimeoutMs = timeoutMs;
+ }
+
+ @Override
+ public void onUndoShown(@NonNull final View view, final int position) {
+ super.onUndoShown(view, position);
+ TimeoutRunnable timeoutRunnable = new TimeoutRunnable(position);
+ mRunnables.put(position, timeoutRunnable);
+ mHandler.postDelayed(timeoutRunnable, mTimeoutMs);
+ }
+
+ @Override
+ public void onUndo(@NonNull final View view, final int position) {
+ super.onUndo(view, position);
+ cancelCallback(position);
+ }
+
+ @Override
+ public void onDismiss(@NonNull final View view, final int position) {
+ super.onDismiss(view, position);
+ cancelCallback(position);
+ }
+
+ @Override
+ public void dismiss(final int position) {
+ super.dismiss(position);
+ cancelCallback(position);
+ }
+
+ private void cancelCallback(final int position) {
+ Runnable timeoutRunnable = mRunnables.get(position);
+ if (timeoutRunnable != null) {
+ mHandler.removeCallbacks(timeoutRunnable);
+ mRunnables.remove(position);
+ }
+ }
+
+ @Override
+ public void onDismiss(@NonNull final ViewGroup listView, @NonNull final int[] reverseSortedPositions) {
+ super.onDismiss(listView, reverseSortedPositions);
+
+ /* Adjust the pending timeout positions accordingly wrt the given dismissed positions */
+ //noinspection UseSparseArrays
+ Map newRunnables = new HashMap<>();
+ for (int position : reverseSortedPositions) {
+ for (int key : mRunnables.keySet()) {
+ TimeoutRunnable runnable = mRunnables.get(key);
+ if (key > position) {
+ key--;
+ runnable.setPosition(key);
+ newRunnables.put(key, runnable);
+ } else if (key != position) {
+ newRunnables.put(key, runnable);
+ }
+ }
+
+ mRunnables.clear();
+ mRunnables.putAll(newRunnables);
+ newRunnables.clear();
+ }
+ }
+
+ /**
+ * A {@link Runnable} class which dismisses a position when executed.
+ */
+ private class TimeoutRunnable implements Runnable {
+
+ private int mPosition;
+
+ TimeoutRunnable(final int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public void run() {
+ dismiss(mPosition);
+ }
+
+ public int getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(final int position) {
+ mPosition = position;
+ }
+ }
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoAdapter.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoAdapter.java
new file mode 100644
index 00000000..c9d6c1b2
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * An interface used for {@link SimpleSwipeUndoAdapter}.
+ * Used to provide the undo {@link android.view.View}s.
+ */
+public interface UndoAdapter {
+
+ /**
+ * Returns the entire undo {@link android.view.View} that should be shown.
+ *
+ * @param position the position of the item for which the undo {@code View} should be shown.
+ * @param convertView The old view to reuse, if possible. Note: You should check that this view
+ * is non-null and of an appropriate type before using. If it is not possible to convert
+ * this view to display the correct data, this method can create a new view.
+ * @param parent The parent that this view will eventually be attached to
+ */
+ @NonNull
+ View getUndoView(final int position, @Nullable final View convertView, @NonNull final ViewGroup parent);
+
+ /**
+ * Returns the {@link android.view.View} which serves as a button to undo the swipe movement. When a user clicks on this {@code View}, the swipe is undone.
+ *
+ * @param view the parent {@code View} as returned in {@link #getUndoView(int, android.view.View, android.view.ViewGroup)}.
+ */
+ @NonNull
+ View getUndoClickView(@NonNull final View view);
+
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoCallback.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoCallback.java
new file mode 100644
index 00000000..13996bf2
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/UndoCallback.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.support.annotation.NonNull;
+import android.view.View;
+
+import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.OnDismissCallback;
+
+/**
+ * A callback interface used to inform its client about a successful dismissal of one or more list item positions.
+ */
+public interface UndoCallback extends OnDismissCallback {
+
+ /**
+ * Returns the primary {@link android.view.View} contained in given {@code View}.
+ *
+ * @param view the parent {@code View}, which contains both the primary and the undo {@link android.view.View}s.
+ */
+ @NonNull
+ View getPrimaryView(@NonNull View view);
+
+ /**
+ * Returns the undo {@link android.view.View} contained in given {@code View}.
+ *
+ * @param view the parent {@code View}, which contains both the primary and the undo {@link android.view.View}s.
+ */
+ @NonNull
+ View getUndoView(@NonNull View view);
+
+ /**
+ * Called when the undo {@link android.view.View} is shown for given position.
+ *
+ * @param view the parent {@code View}, which contains both the primary and the undo {@link android.view.View}s.
+ * @param position the position for which the undo {@code View} is shown.
+ */
+ void onUndoShown(@NonNull View view, int position);
+
+ /**
+ * Called when the undo button is clicked for given position, and the primary {@link View} is shown.
+ *
+ * @param view the parent {@code View}, which contains both the primary and the undo {@link android.view.View}s.
+ * @param position the position for which the button has been clicked.
+ */
+ void onUndo(@NonNull View view, int position);
+
+ /**
+ * Called when the user has definitively dismissed an item.
+ * Do NOT remove the item from the adapter here! Instead, do this in {@link #onDismiss(android.view.ViewGroup, int[])}.
+ *
+ * @param view the parent {@code View}, which contains both the primary and the undo {@link android.view.View}s.
+ * @param position the position of the item that is dismissed.
+ */
+ void onDismiss(@NonNull View view, int position);
+}
diff --git a/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/Util.java b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/Util.java
new file mode 100644
index 00000000..2f35d581
--- /dev/null
+++ b/lib-manipulation/src/main/java/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/undo/Util.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.undo;
+
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+class Util {
+
+ private Util() {
+ }
+
+ @NonNull
+ static Collection processDeletions(@NonNull final Collection positions, @NonNull final int[] dismissedPositions) {
+ List dismissedList = new ArrayList<>();
+ for (int position : dismissedPositions) {
+ dismissedList.add(position);
+ }
+ return processDeletions(positions, dismissedList);
+ }
+
+ /**
+ * Removes positions in {@code dismissedPositions} from {@code positions}, and shifts the remaining positions accordingly.
+ *
+ * @param positions the list of positions to remove from.
+ * @param dismissedPositions the list of positions to remove.
+ *
+ * @return a new {@link java.util.Collection} instance, containing the resulting positions.
+ */
+ @NonNull
+ static Collection processDeletions(@NonNull final Collection positions, @NonNull final List dismissedPositions) {
+ Collection result = new ArrayList<>(positions);
+ Collections.sort(dismissedPositions, Collections.reverseOrder());
+ Collection newUndoPositions = new ArrayList<>();
+ for (int position : dismissedPositions) {
+ for (Iterator iterator = result.iterator(); iterator.hasNext(); ) {
+ int undoPosition = iterator.next();
+ if (undoPosition > position) {
+ iterator.remove();
+ newUndoPositions.add(undoPosition - 1);
+ } else if (undoPosition == position) {
+ iterator.remove();
+ } else {
+ newUndoPositions.add(undoPosition);
+ }
+ }
+ result.clear();
+ result.addAll(newUndoPositions);
+ newUndoPositions.clear();
+ }
+
+ return result;
+ }
+}
diff --git a/library/AndroidManifest.xml b/library/AndroidManifest.xml
deleted file mode 100644
index 3aa03627..00000000
--- a/library/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
deleted file mode 100644
index 8134bdc2..00000000
--- a/library/build.gradle
+++ /dev/null
@@ -1,44 +0,0 @@
-apply plugin: 'android-library'
-
-
-dependencies {
- compile 'com.nineoldandroids:library:2.4.0'
-
- instrumentTestCompile ('org.mockito:mockito-core:1.9.5') { exclude group: 'org.hamcrest' }
- instrumentTestCompile ('com.google.dexmaker:dexmaker-mockito:1.0') { exclude group: 'org.hamcrest' }
- instrumentTestCompile ('junit:junit:4.11') { exclude group: 'org.hamcrest' }
- instrumentTestCompile 'org.hamcrest:hamcrest-all:1.3'
-}
-
-android {
- compileSdkVersion 19
- buildToolsVersion '19.0.1'
-
- sourceSets {
- main {
- manifest.srcFile 'AndroidManifest.xml'
- java.srcDirs = ['src']
- resources.srcDirs = ['src']
- res.srcDirs = ['res']
- assets.srcDirs = ['assets']
- }
-
- instrumentTest.setRoot('tests')
- instrumentTest {
- java.srcDirs = ['tests/java']
- }
- }
-
- defaultConfig {
- minSdkVersion 8
- targetSdkVersion 19
- versionName project.VERSION_NAME
- versionCode Integer.parseInt(new Date().format('yyyyMMddHH'))
- }
-
- packagingOptions {
- exclude 'LICENSE.txt'
- }
-}
-
-apply from: '../maven_push.gradle'
\ No newline at end of file
diff --git a/library/gradle.properties b/library/gradle.properties
deleted file mode 100644
index f7d39113..00000000
--- a/library/gradle.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-POM_NAME=ListViewAnimations Library
-POM_ARTIFACT_ID=library
-POM_PACKAGING=aar
\ No newline at end of file
diff --git a/library/libs/nineoldandroids-2.4.0.jar b/library/libs/nineoldandroids-2.4.0.jar
deleted file mode 100644
index 43ee45ff..00000000
Binary files a/library/libs/nineoldandroids-2.4.0.jar and /dev/null differ
diff --git a/library/libs/stickylistheaders_lib.jar b/library/libs/stickylistheaders_lib.jar
deleted file mode 100644
index ce4123ef..00000000
Binary files a/library/libs/stickylistheaders_lib.jar and /dev/null differ
diff --git a/library/lint.xml b/library/lint.xml
deleted file mode 100644
index ee0eead5..00000000
--- a/library/lint.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/library/pom.xml b/library/pom.xml
deleted file mode 100644
index 8e8555e6..00000000
--- a/library/pom.xml
+++ /dev/null
@@ -1,142 +0,0 @@
-
- 4.0.0
-
-
- com.haarman.listviewanimations
- parent
- 1.0.0-SNAPSHOT
-
-
- listviewanimations
- apklib
-
- ListViewAnimations (Library)
-
-
-
-
- com.google.android
- android
- provided
-
-
-
-
- com.nineoldandroids
- library
-
-
-
-
- junit
- junit
- test
-
-
-
-
- src
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- true
-
-
- runLint
- compile
-
- lint
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
-
-
- package
-
- attach-artifact
-
-
-
-
- jar
- ${project.build.directory}/${project.build.finalName}.jar
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- attach-javadocs
-
- jar
-
-
- true
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
-
-
-
-
-
-
-
- release
-
-
- performRelease
- true
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- ${maven-gpg-plugin.version}
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/ArrayAdapter.java b/library/src/com/nhaarman/listviewanimations/ArrayAdapter.java
deleted file mode 100644
index 84e16bbb..00000000
--- a/library/src/com/nhaarman/listviewanimations/ArrayAdapter.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations;
-
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.itemmanipulation.AnimateAdditionAdapter;
-import com.nhaarman.listviewanimations.widget.DynamicListView;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * A {@code true} {@link ArrayList} adapter providing access to all ArrayList methods.
- * Also implements {@link DynamicListView.Swappable} for easy object swapping, and {@link AnimateAdditionAdapter.Insertable} for inserting objects.
- */
-@SuppressWarnings("UnusedDeclaration")
-public abstract class ArrayAdapter extends BaseAdapter implements List, DynamicListView.Swappable, AnimateAdditionAdapter.Insertable {
-
- protected List mItems;
-
- /**
- * Creates a new ArrayAdapter with an empty {@code List} .
- */
- public ArrayAdapter() {
- this(null);
- }
-
- /**
- * Creates a new {@link ArrayAdapter} using given {@code List} , or an empty {@code List} if objects == null.
- */
- public ArrayAdapter(final List objects) {
- this(objects, false);
- }
-
- /**
- * Creates a new {@link ArrayAdapter}, using (a copy of) given {@code List} , or an empty {@code List} if objects = null.
- * @param copyList {@code true} to create a copy of the {@code List} , {@code false} to reuse the reference.
- */
- public ArrayAdapter(final List objects, final boolean copyList) {
- if (objects != null) {
- if (copyList) {
- mItems = new ArrayList(objects);
- } else {
- mItems = objects;
- }
- } else {
- mItems = new ArrayList();
- }
- }
-
-
- @Override
- public int getCount() {
- return mItems.size();
- }
-
- @Override
- public T getItem(final int location) {
- return mItems.get(location);
- }
-
- @Override
- public long getItemId(final int location) {
- return location;
- }
-
- /**
- * Appends the specified element to the end of the {@code List} .
- * @param object the object to add.
- * @return always true.
- */
- @Override
- public boolean add(final T object) {
- boolean result = mItems.add(object);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public void add(final int location, final T object) {
- mItems.add(location, object);
- notifyDataSetChanged();
- }
-
- /**
- * Adds the objects in the specified collection to the end of this List. The objects are added in the order in which they are returned from the collection's iterator.
- * @param collection the collection of objects.
- * @return {@code true} if this {@code List} is modified, {@code false} otherwise.
- */
- @Override
- public boolean addAll(final Collection extends T> collection) {
- boolean result = mItems.addAll(collection);
- notifyDataSetChanged();
- return result;
- }
-
- /**
- * Appends all of the elements in the specified collection to the end of the
- * {@code List} , in the order that they are specified.
- * @param objects the array of objects.
- * @return {@code true} if the collection changed during insertion.
- */
- public boolean addAll(final T... objects) {
- boolean result = Collections.addAll(mItems, objects);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public boolean addAll(final int location, final Collection extends T> objects) {
- boolean result = mItems.addAll(location, objects);
- notifyDataSetChanged();
- return result;
- }
-
- /**
- * Inserts the objects in the specified collection at the specified location in this List. The objects are added in the order that they specified.
- * @param location the index at which to insert.
- * @param objects the array of objects.
- */
- public void addAll(final int location, final T... objects) {
- for (int i = location; i < objects.length + location; i++) {
- mItems.add(i, objects[i]);
- }
- notifyDataSetChanged();
- }
-
- @Override
- public void clear() {
- mItems.clear();
- notifyDataSetChanged();
- }
-
- @Override
- public boolean contains(final Object object) {
- return mItems.contains(object);
- }
-
- @Override
- public boolean containsAll(final Collection> collection) {
- return mItems.containsAll(collection);
- }
-
- @Override
- public T get(final int location) {
- return mItems.get(location);
- }
-
- @Override
- public T set(final int location, final T object) {
- T result = mItems.set(location, object);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public int size() {
- return mItems.size();
- }
-
- @Override
- public List subList(final int start, final int end) {
- return mItems.subList(start, end);
- }
-
- @Override
- public Object[] toArray() {
- return mItems.toArray();
- }
-
- @Override
- public T1[] toArray(final T1[] array) {
- //noinspection SuspiciousToArrayCall
- return mItems.toArray(array);
- }
-
- @Override
- public boolean remove(final Object object) {
- boolean result = mItems.remove(object);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public T remove(final int location) {
- T result = mItems.remove(location);
- notifyDataSetChanged();
- return result;
- }
-
- /**
- * Removes all elements at the specified locations in the {@code List} .
- * @param locations the collection of indexes to remove.
- * @return a collection containing the removed objects.
- */
- public Collection removePositions(final Collection locations) {
- ArrayList removedItems = new ArrayList();
-
- ArrayList locationsList = new ArrayList(locations);
- Collections.sort(locationsList);
- Collections.reverse(locationsList);
- for (int location : locationsList) {
- removedItems.add(mItems.remove(location));
- }
- notifyDataSetChanged();
- return removedItems;
- }
-
- @Override
- public boolean removeAll(final Collection> objects) {
- boolean result = mItems.removeAll(objects);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public boolean retainAll(final Collection> objects) {
- boolean result = mItems.retainAll(objects);
- notifyDataSetChanged();
- return result;
- }
-
- @Override
- public int indexOf(final Object object) {
- return mItems.indexOf(object);
- }
-
- @Override
- public Iterator iterator() {
- return mItems.iterator();
- }
-
- @Override
- public int lastIndexOf(final Object object) {
- return mItems.lastIndexOf(object);
- }
-
- @Override
- public ListIterator listIterator() {
- return mItems.listIterator();
- }
-
- @Override
- public ListIterator listIterator(final int location) {
- return mItems.listIterator(location);
- }
-
- @Override
- public void swapItems(final int locationOne, final int locationTwo) {
- T temp = getItem(locationOne);
- set(locationOne, getItem(locationTwo));
- set(locationTwo, temp);
- }
-
- private BaseAdapter mDataSetChangedSlavedAdapter;
-
- public void propagateNotifyDataSetChanged(final BaseAdapter slavedAdapter) {
- mDataSetChangedSlavedAdapter = slavedAdapter;
- }
-
- @Override
- public void notifyDataSetChanged() {
- super.notifyDataSetChanged();
- if (mDataSetChangedSlavedAdapter != null) {
- mDataSetChangedSlavedAdapter.notifyDataSetChanged();
- }
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/BaseAdapterDecorator.java b/library/src/com/nhaarman/listviewanimations/BaseAdapterDecorator.java
deleted file mode 100644
index 44aa2e15..00000000
--- a/library/src/com/nhaarman/listviewanimations/BaseAdapterDecorator.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations;
-
-import android.database.DataSetObserver;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.SectionIndexer;
-
-import com.nhaarman.listviewanimations.widget.DynamicListView;
-import com.nhaarman.listviewanimations.widget.DynamicListView.Swappable;
-
-/**
- * A decorator class that enables decoration of an instance of the BaseAdapter
- * class.
- *
- * Classes extending this class can override methods and provide extra
- * functionality before or after calling the super method.
- */
-public abstract class BaseAdapterDecorator extends BaseAdapter implements SectionIndexer, DynamicListView.Swappable, ListViewSetter {
-
- protected final BaseAdapter mDecoratedBaseAdapter;
-
- private AbsListView mListView;
-
- private boolean mIsParentHorizontalScrollContainer;
- private int mResIdTouchChild;
-
- public BaseAdapterDecorator(final BaseAdapter baseAdapter) {
- mDecoratedBaseAdapter = baseAdapter;
- }
-
- @Override
- public void setAbsListView(final AbsListView listView) {
- mListView = listView;
-
- if (mDecoratedBaseAdapter instanceof ListViewSetter) {
- ((ListViewSetter) mDecoratedBaseAdapter).setAbsListView(listView);
- }
-
- if (mListView instanceof DynamicListView) {
- DynamicListView dynListView = (DynamicListView) mListView;
- dynListView.setIsParentHorizontalScrollContainer(mIsParentHorizontalScrollContainer);
- dynListView.setDynamicTouchChild(mResIdTouchChild);
- }
- }
-
- public AbsListView getAbsListView() {
- return mListView;
- }
-
- @Override
- public int getCount() {
- return mDecoratedBaseAdapter.getCount();
- }
-
- @Override
- public Object getItem(final int position) {
- return mDecoratedBaseAdapter.getItem(position);
- }
-
- @Override
- public long getItemId(final int position) {
- return mDecoratedBaseAdapter.getItemId(position);
- }
-
- @Override
- public View getView(final int position, final View convertView, final ViewGroup parent) {
- return mDecoratedBaseAdapter.getView(position, convertView, parent);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return mDecoratedBaseAdapter.areAllItemsEnabled();
- }
-
- @Override
- public View getDropDownView(final int position, final View convertView, final ViewGroup parent) {
- return mDecoratedBaseAdapter.getDropDownView(position, convertView, parent);
- }
-
- @Override
- public int getItemViewType(final int position) {
- return mDecoratedBaseAdapter.getItemViewType(position);
- }
-
- @Override
- public int getViewTypeCount() {
- return mDecoratedBaseAdapter.getViewTypeCount();
- }
-
- @Override
- public boolean hasStableIds() {
- return mDecoratedBaseAdapter.hasStableIds();
- }
-
- @Override
- public boolean isEmpty() {
- return mDecoratedBaseAdapter.isEmpty();
- }
-
- @Override
- public boolean isEnabled(final int position) {
- return mDecoratedBaseAdapter.isEnabled(position);
- }
-
- @Override
- public void notifyDataSetChanged() {
- if (!(mDecoratedBaseAdapter instanceof ArrayAdapter>)) {
- // fix #35 dirty trick !
- // leads to an infinite loop when trying because ArrayAdapter triggers notifyDataSetChanged itself
- mDecoratedBaseAdapter.notifyDataSetChanged();
- }
- }
-
- /**
- * Helper function if you want to force notifyDataSetChanged()
- */
- @SuppressWarnings("UnusedDeclaration")
- public void notifyDataSetChanged(final boolean force) {
- if (force || !(mDecoratedBaseAdapter instanceof ArrayAdapter>)) {
- // leads to an infinite loop when trying because ArrayAdapter triggers notifyDataSetChanged itself
- mDecoratedBaseAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void notifyDataSetInvalidated() {
- mDecoratedBaseAdapter.notifyDataSetInvalidated();
- }
-
- @Override
- public void registerDataSetObserver(final DataSetObserver observer) {
- mDecoratedBaseAdapter.registerDataSetObserver(observer);
- }
-
- @Override
- public void unregisterDataSetObserver(final DataSetObserver observer) {
- mDecoratedBaseAdapter.unregisterDataSetObserver(observer);
- }
-
- @Override
- public int getPositionForSection(final int section) {
- if (mDecoratedBaseAdapter instanceof SectionIndexer) {
- return ((SectionIndexer) mDecoratedBaseAdapter).getPositionForSection(section);
- }
- return 0;
- }
-
- @Override
- public int getSectionForPosition(final int position) {
- if (mDecoratedBaseAdapter instanceof SectionIndexer) {
- return ((SectionIndexer) mDecoratedBaseAdapter).getSectionForPosition(position);
- }
- return 0;
- }
-
- @Override
- public Object[] getSections() {
- if (mDecoratedBaseAdapter instanceof SectionIndexer) {
- return ((SectionIndexer) mDecoratedBaseAdapter).getSections();
- }
- return null;
- }
-
- public BaseAdapter getDecoratedBaseAdapter() {
- return mDecoratedBaseAdapter;
- }
-
- @Override
- public void swapItems(final int positionOne, final int positionTwo) {
- if (mDecoratedBaseAdapter instanceof Swappable) {
- ((Swappable) mDecoratedBaseAdapter).swapItems(positionOne, positionTwo);
- }
- }
-
- /**
- * If the adapter's list-view is hosted inside a parent(/grand-parent/etc) that can scroll horizontally, horizontal swipes won't
- * work, because the parent will prevent touch-events from reaching the list-view.
- *
- * Call this method with the value 'true' to fix this behavior.
- * Note that this will prevent the parent from scrolling horizontally when the user touches anywhere in a list-item.
- */
- public void setIsParentHorizontalScrollContainer(final boolean isParentHorizontalScrollContainer) {
- mIsParentHorizontalScrollContainer = isParentHorizontalScrollContainer;
- if (mListView instanceof DynamicListView) {
- DynamicListView dynListView = (DynamicListView) mListView;
- dynListView.setIsParentHorizontalScrollContainer(mIsParentHorizontalScrollContainer);
- }
- }
-
- public boolean isParentHorizontalScrollContainer() {
- return mIsParentHorizontalScrollContainer;
- }
-
- /**
- * If the adapter's list-view is hosted inside a parent(/grand-parent/etc) that can scroll horizontally, horizontal swipes won't
- * work, because the parent will prevent touch-events from reaching the list-view.
- *
- * If a list-item view has a child with the given resource-ID, the user can still swipe the list-item by touching that child.
- * If the user touches an area outside that child (but inside the list-item view), then the swipe will not happen and the parent
- * will do its job instead (scrolling horizontally).
- *
- * @param childResId The resource-ID of the list-items' child that the user should touch to be able to swipe the list-items.
- */
- public void setTouchChild(final int childResId) {
- mResIdTouchChild = childResId;
- if (mListView instanceof DynamicListView) {
- DynamicListView dynListView = (DynamicListView) mListView;
- dynListView.setDynamicTouchChild(mResIdTouchChild);
- }
- }
-
- public int getTouchChild() {
- return mResIdTouchChild;
- }
-}
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/ListViewSetter.java b/library/src/com/nhaarman/listviewanimations/ListViewSetter.java
deleted file mode 100644
index a4972caa..00000000
--- a/library/src/com/nhaarman/listviewanimations/ListViewSetter.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.nhaarman.listviewanimations;
-
-import android.widget.AbsListView;
-
-public interface ListViewSetter {
-
- void setAbsListView(AbsListView listView);
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/AnimateDismissAdapter.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/AnimateDismissAdapter.java
deleted file mode 100644
index 1bd84bdd..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/AnimateDismissAdapter.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.BaseAdapterDecorator;
-import com.nhaarman.listviewanimations.util.AdapterViewUtil;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorListenerAdapter;
-import com.nineoldandroids.animation.AnimatorSet;
-import com.nineoldandroids.animation.ValueAnimator;
-import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A {@link BaseAdapterDecorator} class that provides animations to the removal
- * of items in the given {@link BaseAdapter}.
- */
-public class AnimateDismissAdapter extends BaseAdapterDecorator {
-
- private final OnDismissCallback mCallback;
-
- /**
- * Create a new AnimateDismissAdapter based on the given {@link BaseAdapter}.
- *
- * @param callback
- * The {@link OnDismissCallback} to trigger when the user has
- * indicated that she would like to dismiss one or more list
- * items.
- */
- public AnimateDismissAdapter(final BaseAdapter baseAdapter, final OnDismissCallback callback) {
- super(baseAdapter);
- mCallback = callback;
- }
-
- /**
- * Animate dismissal of the item at given position.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void animateDismiss(final int position) {
- animateDismiss(Arrays.asList(position));
- }
-
- /**
- * Animate dismissal of the items at given positions.
- */
- public void animateDismiss(final Collection positions) {
- final List positionsCopy = new ArrayList(positions);
- if (getAbsListView() == null) {
- throw new IllegalStateException("Call setAbsListView() on this AnimateDismissAdapter before calling setAdapter()!");
- }
-
- List views = getVisibleViewsForPositions(positionsCopy);
-
- if (!views.isEmpty()) {
- List animators = new ArrayList();
- for (final View view : views) {
- animators.add(createAnimatorForView(view));
- }
-
- AnimatorSet animatorSet = new AnimatorSet();
-
- Animator[] animatorsArray = new Animator[animators.size()];
- for (int i = 0; i < animatorsArray.length; i++) {
- animatorsArray[i] = animators.get(i);
- }
-
- animatorSet.playTogether(animatorsArray);
- animatorSet.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(final Animator animator) {
- invokeCallback(positionsCopy);
- }
- });
- animatorSet.start();
- } else {
- invokeCallback(positionsCopy);
- }
- }
-
- private void invokeCallback(final Collection positions) {
- ArrayList positionsList = new ArrayList(positions);
- Collections.sort(positionsList);
-
- int[] dismissPositions = new int[positionsList.size()];
- for (int i = 0; i < positionsList.size(); i++) {
- dismissPositions[i] = positionsList.get(positionsList.size() - 1 - i);
- }
- mCallback.onDismiss(getAbsListView(), dismissPositions);
- }
-
- private List getVisibleViewsForPositions(final Collection positions) {
- List views = new ArrayList();
- for (int i = 0; i < getAbsListView().getChildCount(); i++) {
- View child = getAbsListView().getChildAt(i);
- if (positions.contains(AdapterViewUtil.getPositionForView(getAbsListView(), child))) {
- views.add(child);
- }
- }
- return views;
- }
-
- private Animator createAnimatorForView(final View view) {
- final ViewGroup.LayoutParams lp = view.getLayoutParams();
- final int originalHeight = view.getHeight();
-
- ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 0);
- animator.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(final Animator animator) {
- lp.height = 0;
- view.setLayoutParams(lp);
- }
- });
-
- animator.addUpdateListener(new AnimatorUpdateListener() {
-
- @Override
- public void onAnimationUpdate(final ValueAnimator valueAnimator) {
- lp.height = (Integer) valueAnimator.getAnimatedValue();
- view.setLayoutParams(lp);
- }
- });
-
- return animator;
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/ExpandCollapseListener.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/ExpandCollapseListener.java
deleted file mode 100644
index 07bc638c..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/ExpandCollapseListener.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.nhaarman.listviewanimations.itemmanipulation;
-
-// TODO integrate in ExpandableListItemAdapter
-public interface ExpandCollapseListener {
-
- public void onItemExpanded(int position);
-
- public void onItemCollapsed(int position);
-
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/OnDismissCallback.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/OnDismissCallback.java
deleted file mode 100644
index 752b6008..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/OnDismissCallback.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation;
-
-import android.widget.AbsListView;
-import android.widget.ListView;
-
-/**
- * The callback interface used by {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissListViewTouchListener} to
- * inform its client about a successful dismissal of one or more list item
- * positions.
- */
-public interface OnDismissCallback {
- /**
- * Called when the user has indicated they she would like to dismiss one or
- * more list item positions.
- *
- * @param listView
- * The originating {@link ListView}.
- * @param reverseSortedPositions
- * An array of positions to dismiss, sorted in descending order
- * for convenience.
- */
- void onDismiss(AbsListView listView, int[] reverseSortedPositions);
-}
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java
deleted file mode 100644
index 82e2a622..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/DismissableManager.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
-
-/**
- * An interface to specify whether certain items can or cannot be dismissed.
- */
-public interface DismissableManager {
-
- /**
- * Returns whether the item for given id and position can be dismissed.
- * @param id the id of the item.
- * @param position the position of the item.
- * @return true if the item can be dismissed, false otherwise.
- */
- public boolean isDismissable(long id, int position);
-}
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java
deleted file mode 100644
index bb8dffd8..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissAdapter.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
-
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.ArrayAdapter;
-import com.nhaarman.listviewanimations.BaseAdapterDecorator;
-import com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback;
-
-/**
- * Adds an option to swipe items in an AbsListView away.
- * Do not call {@link android.widget.AbsListView#setOnTouchListener(android.view.View.OnTouchListener)} or
- * {@link android.widget.AbsListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)} on your AbsListView! To use an {@link android.widget.AbsListView.OnScrollListener},
- * extends {@link SwipeOnScrollListener} and
- * pass it in the constructor {@link #SwipeDismissAdapter(android.widget.BaseAdapter, com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback, SwipeOnScrollListener)}.
- */
-public class SwipeDismissAdapter extends BaseAdapterDecorator {
-
- protected OnDismissCallback mOnDismissCallback;
- protected SwipeDismissListViewTouchListener mSwipeDismissListViewTouchListener;
- protected SwipeOnScrollListener mSwipeOnScrollListener;
-
- /**
- * Create a new SwipeDismissAdapter.
- *
- * @param baseAdapter the {@link android.widget.BaseAdapter to use}
- * @param onDismissCallback the {@link OnDismissCallback} to be notified of dismissed items.
- */
- public SwipeDismissAdapter(final BaseAdapter baseAdapter, final OnDismissCallback onDismissCallback) {
- this(baseAdapter, onDismissCallback, new SwipeOnScrollListener());
- }
-
- /**
- * Create a new SwipeDismissAdapter.
- *
- * @param baseAdapter the {@link android.widget.BaseAdapter to use}
- * @param onDismissCallback the {@link OnDismissCallback} to be notified of dismissed items.
- * @param swipeOnScrollListener the {@link SwipeOnScrollListener} to use.
- */
- public SwipeDismissAdapter(final BaseAdapter baseAdapter, final OnDismissCallback onDismissCallback, final SwipeOnScrollListener swipeOnScrollListener) {
- super(baseAdapter);
- mOnDismissCallback = onDismissCallback;
- mSwipeOnScrollListener = swipeOnScrollListener;
- }
-
- protected SwipeDismissListViewTouchListener createListViewTouchListener(final AbsListView listView) {
- return new SwipeDismissListViewTouchListener(listView, mOnDismissCallback, mSwipeOnScrollListener);
- }
-
- @Override
- public void setAbsListView(final AbsListView listView) {
- super.setAbsListView(listView);
- if (mDecoratedBaseAdapter instanceof ArrayAdapter>) {
- ((ArrayAdapter>) mDecoratedBaseAdapter).propagateNotifyDataSetChanged(this);
- }
- mSwipeDismissListViewTouchListener = createListViewTouchListener(listView);
- mSwipeDismissListViewTouchListener.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer());
- mSwipeDismissListViewTouchListener.setTouchChild(getTouchChild());
- listView.setOnTouchListener(mSwipeDismissListViewTouchListener);
- }
-
- @Override
- public void setIsParentHorizontalScrollContainer(final boolean isParentHorizontalScrollContainer) {
- super.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer);
- if (mSwipeDismissListViewTouchListener != null) {
- mSwipeDismissListViewTouchListener.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer);
- }
- }
-
- @Override
- public void notifyDataSetChanged() {
- super.notifyDataSetChanged();
- if (mSwipeDismissListViewTouchListener != null) {
- mSwipeDismissListViewTouchListener.notifyDataSetChanged();
- }
- }
-
- @Override
- public void setTouchChild(final int childResId) {
- super.setTouchChild(childResId);
- if (mSwipeDismissListViewTouchListener != null) {
- mSwipeDismissListViewTouchListener.setTouchChild(childResId);
- }
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissListViewTouchListener.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissListViewTouchListener.java
deleted file mode 100644
index a5cfbd3a..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeDismissListViewTouchListener.java
+++ /dev/null
@@ -1,469 +0,0 @@
-// THIS IS A BETA! I DON'T RECOMMEND USING IT IN PRODUCTION CODE JUST YET
-
-/*
- * Copyright 2012 Roman Nurik
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
-
-import android.annotation.SuppressLint;
-import android.graphics.Rect;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.ListView;
-
-import com.nhaarman.listviewanimations.itemmanipulation.OnDismissCallback;
-import com.nhaarman.listviewanimations.util.AdapterViewUtil;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorListenerAdapter;
-import com.nineoldandroids.animation.ValueAnimator;
-import com.nineoldandroids.view.ViewHelper;
-import com.nineoldandroids.view.ViewPropertyAnimator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
-
-/**
- * A {@link android.view.View.OnTouchListener} that makes the list items in a
- * {@link ListView} dismissable. {@link ListView} is given special treatment
- * because by default it handles touches for its list items... i.e. it's in
- * charge of drawing the pressed state (the list selector), handling list item
- * clicks, etc.
- *
- * For performance reasons, do not use this class directly, but use the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissAdapter}.
- */
-@SuppressLint("Recycle")
-public class SwipeDismissListViewTouchListener implements SwipeOnTouchListener {
-
- private static final int MIN_FLING_VELOCITY_FACTOR = 16;
- // Cached ViewConfiguration and system-wide constant values
- private final int mSlop;
- private final int mMinFlingVelocity;
- private final int mMaxFlingVelocity;
- protected long mAnimationTime;
-
- // Fixed properties
- private final AbsListView mListView;
- private final OnDismissCallback mCallback;
- private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
-
- // Transient properties
- protected List mPendingDismisses = new ArrayList();
- private int mDismissAnimationRefCount = 0;
- private float mDownX;
- private float mDownY;
- private boolean mSwiping;
-
- private VelocityTracker mVelocityTracker;
- private PendingDismissData mCurrentDismissData;
-
- private int mVirtualListCount = -1;
-
- private boolean mDisallowSwipe;
- private boolean mIsParentHorizontalScrollContainer;
- private int mResIdOfTouchChild;
- private boolean mTouchChildTouched;
-
- private DismissableManager mDismissableManager;
-
- /**
- * Constructs a new swipe-to-dismiss touch listener for the given list view.
- *
- * @param listView
- * The list view whose items should be dismissable.
- * @param callback
- * The callback to trigger when the user has indicated that she
- * would like to dismiss one or more list items.
- */
- public SwipeDismissListViewTouchListener(final AbsListView listView, final OnDismissCallback callback, final SwipeOnScrollListener onScroll) {
- ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
- mSlop = vc.getScaledTouchSlop();
- mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * MIN_FLING_VELOCITY_FACTOR;
- mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
- mAnimationTime = listView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
- mListView = listView;
- mCallback = callback;
-
- onScroll.setTouchListener(this);
- mListView.setOnScrollListener(onScroll);
- }
-
- public void disallowSwipe() {
- mDisallowSwipe = true;
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public void allowSwipe() {
- mDisallowSwipe = false;
- }
-
- /**
- * Set the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager} to specify which views can or cannot be swiped.
- * @param dismissableManager null for no restrictions.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setDismissableManager(final DismissableManager dismissableManager) {
- mDismissableManager = dismissableManager;
- }
-
- @Override
- public boolean onTouch(final View view, final MotionEvent motionEvent) {
- if (mVirtualListCount == -1) {
- mVirtualListCount = mListView.getAdapter().getCount();
- }
-
- if (mViewWidth < 2) {
- mViewWidth = mListView.getWidth();
- }
-
- switch (motionEvent.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mDisallowSwipe = false;
- view.onTouchEvent(motionEvent);
- return handleDownEvent(motionEvent);
- case MotionEvent.ACTION_MOVE:
- return handleMoveEvent(motionEvent);
- case MotionEvent.ACTION_CANCEL:
- return handleCancelEvent(motionEvent);
- case MotionEvent.ACTION_UP:
-
- mDisallowSwipe = false;
- mTouchChildTouched = false;
- return handleUpEvent(motionEvent);
-
- }
- return false;
- }
-
- @Override
- public boolean isSwiping() {
- return mSwiping;
- }
-
- /**
- * Factory to allow override of dismiss data
- */
- protected PendingDismissData createPendingDismissData(final int position, final View view) {
- return new PendingDismissData(position, view);
- }
-
- private boolean handleDownEvent(final MotionEvent motionEvent) {
- // Find the child view that was touched (perform a hit test)
- Rect rect = new Rect();
- int childCount = mListView.getChildCount();
- int[] listViewCoords = new int[2];
- mListView.getLocationOnScreen(listViewCoords);
- int x = (int) motionEvent.getRawX() - listViewCoords[0];
- int y = (int) motionEvent.getRawY() - listViewCoords[1];
- View downView = null;
- for (int i = 0; i < childCount && downView == null; i++) {
- View child = mListView.getChildAt(i);
- child.getHitRect(rect);
- if (rect.contains(x, y)) {
- downView = child;
- }
- }
-
- if (downView != null) {
- mDownX = motionEvent.getRawX();
- mDownY = motionEvent.getRawY();
- int downPosition = AdapterViewUtil.getPositionForView(mListView, downView);
-
- if (mDismissableManager != null) {
- long downId = mListView.getAdapter().getItemId(downPosition);
- if (!mDismissableManager.isDismissable(downId, downPosition)) {
- /* Cancel, not dismissable */
- return false;
- }
- }
-
- mCurrentDismissData = createPendingDismissData(downPosition, downView);
-
- if (mPendingDismisses.contains(mCurrentDismissData) || downPosition >= mVirtualListCount) {
- // Cancel, we're already processing this position
- mCurrentDismissData = null;
- return false;
- } else {
- mTouchChildTouched = !mIsParentHorizontalScrollContainer && mResIdOfTouchChild == 0;
-
- if (mResIdOfTouchChild != 0) {
- mIsParentHorizontalScrollContainer = false;
-
- final View childView = downView.findViewById(mResIdOfTouchChild);
- if (childView != null) {
- final Rect childRect = getChildViewRect(mListView, childView);
- if (childRect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) {
- mTouchChildTouched = true;
- mListView.requestDisallowInterceptTouchEvent(true);
- }
- }
- }
-
- if (mIsParentHorizontalScrollContainer) {
- // Do it now and don't wait until the user moves more than
- // the slop factor.
- mTouchChildTouched = true;
- mListView.requestDisallowInterceptTouchEvent(true);
- }
-
- mVelocityTracker = VelocityTracker.obtain();
- mVelocityTracker.addMovement(motionEvent);
- }
- }
- return true;
- }
-
- private Rect getChildViewRect(final View parentView, View childView) {
- final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
- if (parentView == childView) {
- return childRect;
-
- }
-
- ViewGroup parent;
- while ((parent = (ViewGroup) childView.getParent()) != parentView) {
- childRect.offset(parent.getLeft(), parent.getTop());
- childView = parent;
- }
-
- return childRect;
- }
-
- private boolean handleMoveEvent(final MotionEvent motionEvent) {
- if (mVelocityTracker == null) {
- return false;
- }
-
- mVelocityTracker.addMovement(motionEvent);
- float deltaX = motionEvent.getRawX() - mDownX;
- float deltaY = motionEvent.getRawY() - mDownY;
- if (mTouchChildTouched && !mDisallowSwipe && Math.abs(deltaX) > mSlop && Math.abs(deltaX) > Math.abs(deltaY)) {
- mSwiping = true;
- mListView.requestDisallowInterceptTouchEvent(true);
-
- // Cancel ListView's touch (un-highlighting the item)
- MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
- cancelEvent.setAction(MotionEvent.ACTION_CANCEL | motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- mListView.onTouchEvent(cancelEvent);
- }
-
- if (mSwiping) {
- ViewHelper.setTranslationX(mCurrentDismissData.view, deltaX);
- //noinspection MagicNumber
- ViewHelper.setAlpha(mCurrentDismissData.view, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
- return true;
- }
- return false;
- }
-
- @SuppressWarnings("UnusedParameters")
- private boolean handleCancelEvent(final MotionEvent motionEvent) {
- if (mVelocityTracker == null) {
- return false;
- }
-
- if (mCurrentDismissData != null && mSwiping) {
- ViewPropertyAnimator.animate(mCurrentDismissData.view)
- .translationX(0)
- .alpha(1)
- .setDuration(mAnimationTime)
- .setListener(null);
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mDownX = 0;
- mDownY = 0;
- mCurrentDismissData = null;
- mSwiping = false;
- return false;
- }
-
-
- private boolean handleUpEvent(final MotionEvent motionEvent) {
- if (mVelocityTracker == null) {
- return false;
- }
-
- float deltaX = motionEvent.getRawX() - mDownX;
- mVelocityTracker.addMovement(motionEvent);
- mVelocityTracker.computeCurrentVelocity(1000);
- float velocityX = Math.abs(mVelocityTracker.getXVelocity());
- float velocityY = Math.abs(mVelocityTracker.getYVelocity());
- boolean dismiss = false;
- boolean dismissRight = false;
- if (Math.abs(deltaX) > mViewWidth / 2) {
- dismiss = true;
- dismissRight = deltaX > 0;
- } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
- dismiss = true;
- dismissRight = mVelocityTracker.getXVelocity() > 0;
- }
-
- if (mSwiping) {
- if (dismiss) {
- // mDownView gets null'd before animation ends
- final PendingDismissData pendingDismissData = mCurrentDismissData;
- ++mDismissAnimationRefCount;
-
- animate(mCurrentDismissData.view).translationX(dismissRight ? mViewWidth : -mViewWidth).alpha(0).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(final Animator animation) {
- onDismiss(pendingDismissData);
- }
- });
-
- mVirtualListCount--;
- mPendingDismisses.add(mCurrentDismissData);
- } else {
- // cancel
- animate(mCurrentDismissData.view).translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null);
- }
- }
-
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mDownX = 0;
- mDownY = 0;
- mCurrentDismissData = null;
- mSwiping = false;
- return false;
- }
-
- protected class PendingDismissData implements Comparable {
- public final int position;
- public final View view;
-
- public PendingDismissData(final int position, final View view) {
- this.position = position;
- this.view = view;
- }
-
- @Override
- public int compareTo(final PendingDismissData other) {
- // Sort by descending position
- return other.position - position;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + position;
- return result;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (((Object) this).getClass() != obj.getClass())
- return false;
- PendingDismissData other = (PendingDismissData) obj;
- return position == other.position;
- }
-
- }
-
- protected void onDismiss(final PendingDismissData data) {
- // default behaviour
- performDismiss(data);
- }
-
- protected void performDismiss(final PendingDismissData data) {
- // Animate the dismissed list item to zero-height and fire the
- // dismiss callback when all dismissed list item animations have
- // completed.
-
- final ViewGroup.LayoutParams lp = data.view.getLayoutParams();
- final int originalHeight = data.view.getHeight();
-
- ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
-
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(final ValueAnimator valueAnimator) {
- lp.height = (Integer) valueAnimator.getAnimatedValue();
- data.view.setLayoutParams(lp);
- }
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animation) {
- finalizeDismiss();
- }
- });
- animator.start();
- }
-
- /**
- * Here you can manage dismissed View.
- */
- protected void recycleDismissedViewsItems(final List pendingDismisses) {
- ViewGroup.LayoutParams lp;
- for (final PendingDismissData pendingDismiss : pendingDismisses) {
- // Reset view presentation
- ViewHelper.setAlpha(pendingDismiss.view, 1f);
- ViewHelper.setTranslationX(pendingDismiss.view, 0);
- lp = pendingDismiss.view.getLayoutParams();
- lp.height = 0;
- pendingDismiss.view.setLayoutParams(lp);
- }
- }
-
- protected void finalizeDismiss() {
- --mDismissAnimationRefCount;
- if (mDismissAnimationRefCount == 0) {
- // No active animations, process all pending dismisses.
- // Sort by descending position
- Collections.sort(mPendingDismisses);
-
- int[] dismissPositions = new int[mPendingDismisses.size()];
- for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {
- dismissPositions[i] = mPendingDismisses.get(i).position;
- }
- mCallback.onDismiss(mListView, dismissPositions);
-
- recycleDismissedViewsItems(mPendingDismisses);
-
- mPendingDismisses.clear();
- }
- }
-
- void setIsParentHorizontalScrollContainer(final boolean isParentHorizontalScrollContainer) {
- mIsParentHorizontalScrollContainer = mResIdOfTouchChild == 0 && isParentHorizontalScrollContainer;
- }
-
- void setTouchChild(final int childResId) {
- mResIdOfTouchChild = childResId;
- if (childResId != 0) {
- setIsParentHorizontalScrollContainer(false);
- }
- }
-
- public void notifyDataSetChanged() {
- mVirtualListCount = mListView.getAdapter().getCount();
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnScrollListener.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnScrollListener.java
deleted file mode 100644
index f5ea2372..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnScrollListener.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.
- *
- * External default OnScrollListener allowing to custom onScrollListening events like loading more items.
- * Author: David Berlioz
- */
-
-package com.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
-
-import android.widget.AbsListView;
-
-/**
- * An {@link android.widget.AbsListView.OnScrollListener} that is used in conjunction with {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeDismissAdapter}. Override this class to
- * provide a custom implementation of the OnScrollListener. Do not forget to call super on the overridden methods!
- */
-public class SwipeOnScrollListener implements AbsListView.OnScrollListener {
-
- private SwipeDismissListViewTouchListener mTouchListener;
-
- public void setTouchListener(final SwipeDismissListViewTouchListener touchListener) {
- mTouchListener = touchListener;
- }
-
- @Override
- public void onScrollStateChanged(final AbsListView view, final int scrollState) {
- if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
- mTouchListener.disallowSwipe();
- }
- }
-
- @Override
- public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) {
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnTouchListener.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnTouchListener.java
deleted file mode 100644
index c06b29bf..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/SwipeOnTouchListener.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2013 K–NFB Reading Technology, Inc.
- */
-
-package com.nhaarman.listviewanimations.itemmanipulation.swipedismiss;
-
-import android.view.View;
-
-/**
- * An OnTouchListener that should be used when list-view items can be swiped horizontally.
- * @author Anton Spaans on 9/12/13.
- */
-public interface SwipeOnTouchListener extends View.OnTouchListener {
- /**
- * @return true if the user is currently swiping a list-item horizontally.
- */
- public boolean isSwiping();
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoAdapter.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoAdapter.java
deleted file mode 100644
index b13965e4..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoAdapter.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright 2013 Frankie Sardo
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.nhaarman.listviewanimations.BaseAdapterDecorator;
-import com.nhaarman.listviewanimations.util.AdapterViewUtil;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorListenerAdapter;
-import com.nineoldandroids.animation.ObjectAnimator;
-import com.nineoldandroids.animation.ValueAnimator;
-import com.nineoldandroids.view.ViewHelper;
-
-import static com.nineoldandroids.view.ViewHelper.setAlpha;
-import static com.nineoldandroids.view.ViewHelper.setTranslationX;
-import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
-
-/**
- * Warning: a stable id for each item in the adapter is required. The decorated
- * adapter should not try to cast convertView to a particular view. The
- * undoLayout should have the same height as the content row.
- *
- * Usage:
- * * Create a new instance of this class providing the {@link BaseAdapter} to wrap, the undo layout, the undo button id and a {@link DeleteItemCallback}, optionally a delay time millis,
- * a count down TextView res id,
- * , a delay in milliseconds before deleting the item.
- * * Set your {@link ListView} to this ContextualUndoAdapter, and set this ContextualUndoAdapter to your ListView.
- */
-@SuppressWarnings("UnusedDeclaration")
-public class ContextualUndoAdapter extends BaseAdapterDecorator implements ContextualUndoListViewTouchListener.Callback {
-
- private static final int ANIMATION_DURATION = 150;
- private static final String EXTRA_ACTIVE_REMOVED_ID = "removedId";
- private static final String X = "x";
-
- private final int mUndoLayoutId;
- private final int mUndoActionId;
- private final int mCountDownTextViewResId;
- private final int mAutoDeleteDelayMillis;
-
- private long mDismissStartMillis;
-
- private ContextualUndoView mCurrentRemovedView;
- private long mCurrentRemovedId;
-
- private final Handler mHandler;
- private final CountDownRunnable mCountDownRunnable;
-
- private final DeleteItemCallback mDeleteItemCallback;
- private final CountDownFormatter mCountDownFormatter;
-
- private ContextualUndoListViewTouchListener mContextualUndoListViewTouchListener;
-
- /**
- * Create a new ContextualUndoAdapter based on given parameters.
- *
- * @param baseAdapter The {@link BaseAdapter} to wrap
- * @param undoLayoutId The layout resource id to show as undo
- * @param undoActionId The id of the component which undoes the dismissal
- * @param deleteItemCallback The {@link DeleteItemCallback} which is called when an item should be deleted from your collection.
- */
- public ContextualUndoAdapter(final BaseAdapter baseAdapter, final int undoLayoutId, final int undoActionId, final DeleteItemCallback deleteItemCallback) {
- this(baseAdapter, undoLayoutId, undoActionId, -1, -1, deleteItemCallback, null);
- }
-
- /**
- * Create a new ContextualUndoAdapter based on given parameters.
- * Will automatically remove the swiped item after autoDeleteTimeMillis milliseconds.
- *
- * @param baseAdapter The {@link BaseAdapter} to wrap
- * @param undoLayoutResId The layout resource id to show as undo
- * @param undoActionResId The id of the component which undoes the dismissal
- * @param autoDeleteTimeMillis The time in milliseconds that the adapter will wait for he user to hit undo before automatically deleting the item
- * @param deleteItemCallback The {@link DeleteItemCallback} which is called when an item should be deleted from your collection.
- */
- public ContextualUndoAdapter(final BaseAdapter baseAdapter, final int undoLayoutResId, final int undoActionResId, final int autoDeleteTimeMillis, final DeleteItemCallback deleteItemCallback) {
- this(baseAdapter, undoLayoutResId, undoActionResId, autoDeleteTimeMillis, -1, deleteItemCallback, null);
- }
-
- /**
- * Create a new ContextualUndoAdapter based on given parameters.
- * Will automatically remove the swiped item after autoDeleteTimeMillis milliseconds.
- *
- * @param baseAdapter The {@link BaseAdapter} to wrap
- * @param undoLayoutResId The layout resource id to show as undo
- * @param undoActionResId The resource id of the component which undoes the dismissal
- * @param autoDeleteTime The time in milliseconds that adapter will wait for user to hit undo before automatically deleting item
- * @param countDownTextViewResId The resource id of the {@link TextView} in the undoLayoutResId that will show the time left
- * @param deleteItemCallback The {@link DeleteItemCallback} which is called when an item should be deleted from your collection.
- * @param countDownFormatter The {@link CountDownFormatter} which provides text to be shown in the {@link TextView} as specified by countDownTextViewResId
- */
- public ContextualUndoAdapter(final BaseAdapter baseAdapter, final int undoLayoutResId, final int undoActionResId, final int autoDeleteTime, final int countDownTextViewResId,
- final DeleteItemCallback deleteItemCallback, final CountDownFormatter countDownFormatter) {
- super(baseAdapter);
-
- mHandler = new Handler();
- mCountDownRunnable = new CountDownRunnable();
-
- mUndoLayoutId = undoLayoutResId;
- mUndoActionId = undoActionResId;
- mCurrentRemovedId = -1;
- mAutoDeleteDelayMillis = autoDeleteTime;
- mCountDownTextViewResId = countDownTextViewResId;
-
- mDeleteItemCallback = deleteItemCallback;
- mCountDownFormatter = countDownFormatter;
- }
-
- @Override
- public final View getView(final int position, final View convertView, final ViewGroup parent) {
- final ViewHolder vh;
- ContextualUndoView contextualUndoView = (ContextualUndoView) convertView;
- if (contextualUndoView == null) {
- contextualUndoView = new ContextualUndoView(parent.getContext(), mUndoLayoutId, mCountDownTextViewResId);
- contextualUndoView.findViewById(mUndoActionId).setOnClickListener(new UndoListener(contextualUndoView));
- vh = new ViewHolder(contextualUndoView);
- } else {
- vh = ViewHolder.getViewHolder(contextualUndoView);
- }
-
- View contentView = super.getView(position, contextualUndoView.getContentView(), contextualUndoView);
- contextualUndoView.updateContentView(contentView);
-
- long itemId = getItemId(position);
- vh.mItemId = itemId;
-
- if (itemId == mCurrentRemovedId) {
- contextualUndoView.displayUndo();
- long millisLeft = mAutoDeleteDelayMillis - (System.currentTimeMillis() - mDismissStartMillis);
- if (mCountDownFormatter != null) {
- contextualUndoView.updateCountDownTimer(mCountDownFormatter.getCountDownString(millisLeft));
- }
- } else {
- contextualUndoView.displayContentView();
- }
-
- contextualUndoView.setItemId(itemId);
- return contextualUndoView;
- }
-
- @Override
- public void setAbsListView(final AbsListView listView) {
- super.setAbsListView(listView);
- mContextualUndoListViewTouchListener = new ContextualUndoListViewTouchListener(listView, this);
- mContextualUndoListViewTouchListener.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer());
- mContextualUndoListViewTouchListener.setTouchChild(getTouchChild());
- listView.setOnTouchListener(mContextualUndoListViewTouchListener);
- listView.setOnScrollListener(mContextualUndoListViewTouchListener.makeScrollListener());
- listView.setOnHierarchyChangeListener(new HierarchyChangeListener());
- }
-
- @Override
- public void onViewSwiped(final long dismissViewItemId, final int dismissPosition) {
- ContextualUndoView contextualUndoView = getContextualUndoView(dismissViewItemId);
- if (contextualUndoView == null) {
- removePreviousContextualUndoIfPresent();
- mCurrentRemovedView = null;
- mCurrentRemovedId = dismissViewItemId;
- } else if (contextualUndoView.isContentDisplayed()) {
- restoreViewPosition(contextualUndoView);
- contextualUndoView.displayUndo();
- removePreviousContextualUndoIfPresent();
- setCurrentRemovedView(contextualUndoView);
-
- if (mAutoDeleteDelayMillis > 0) {
- startAutoDeleteTimer();
- }
- } else {
- performRemovalIfNecessary();
- }
- }
-
- private ContextualUndoView getContextualUndoView(final long dismissViewItemId) {
- ContextualUndoView contextualUndoView = null;
-
- AbsListView listView = getAbsListView();
- int childCount = listView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = listView.getChildAt(i);
- if (child instanceof ContextualUndoView) {
- ContextualUndoView listItem = (ContextualUndoView) child;
- if (listItem.getItemId() == dismissViewItemId) {
- contextualUndoView = listItem;
- }
- }
- }
- return contextualUndoView;
- }
-
- private void startAutoDeleteTimer() {
- mHandler.removeCallbacks(mCountDownRunnable);
-
- if (mCountDownFormatter != null) {
- mCurrentRemovedView.updateCountDownTimer(mCountDownFormatter.getCountDownString(mAutoDeleteDelayMillis));
- }
-
- mDismissStartMillis = System.currentTimeMillis();
- mHandler.postDelayed(mCountDownRunnable, Math.min(1000, mAutoDeleteDelayMillis));
- }
-
- private void restoreViewPosition(final View view) {
- setAlpha(view, 1f);
- setTranslationX(view, 0);
- }
-
- private void removePreviousContextualUndoIfPresent() {
- if (mCurrentRemovedView != null) {
- performRemovalIfNecessary();
- }
- }
-
- private void setCurrentRemovedView(final ContextualUndoView currentRemovedView) {
- mCurrentRemovedView = currentRemovedView;
- mCurrentRemovedId = currentRemovedView.getItemId();
- }
-
- private void clearCurrentRemovedView() {
- mCurrentRemovedView = null;
- mCurrentRemovedId = -1;
- mHandler.removeCallbacks(mCountDownRunnable);
- }
-
- @Override
- public void onListScrolled() {
- performRemovalIfNecessary();
- }
-
- private void performRemovalIfNecessary() {
- if (mCurrentRemovedId == -1) {
- return;
- }
-
- ContextualUndoView currentRemovedView = getCurrentRemovedView(mCurrentRemovedView, mCurrentRemovedId);
- if (currentRemovedView != null) {
- ValueAnimator animator = ValueAnimator.ofInt(currentRemovedView.getHeight(), 1).setDuration(ANIMATION_DURATION);
-
- RemoveViewAnimatorListenerAdapter listener = new RemoveViewAnimatorListenerAdapter(currentRemovedView, mCurrentRemovedId);
- RemoveViewAnimatorUpdateListener updateListener = new RemoveViewAnimatorUpdateListener(listener);
-
- animator.addListener(listener);
- animator.addUpdateListener(updateListener);
- animator.start();
- } else {
- // The hard way.
- deleteItemGivenId(mCurrentRemovedId);
- }
- clearCurrentRemovedView();
- }
-
- private void deleteItemGivenId(final long deleteItemId) {
- int position = -1;
- int numItems = getCount();
- for (int i = 0; i < numItems; i++) {
- long itemId = getItemId(i);
- if (itemId == deleteItemId) {
- position = i;
- break;
- }
- }
-
- if (position >= 0) {
- mDeleteItemCallback.deleteItem(position);
- }
- }
-
- private ContextualUndoView getCurrentRemovedView(final ContextualUndoView currentRemovedView, final long itemId) {
- ContextualUndoView result = currentRemovedView;
- if (result == null ||
- result.getParent() == null ||
- result.getItemId() != itemId ||
- AdapterViewUtil.getPositionForView(getAbsListView(), result) < 0) {
- result = getContextualUndoView(itemId);
- }
- return result;
- }
-
- /**
- * This method should be called in your {@link Activity}'s {@link Activity#onSaveInstanceState(Bundle)} to remember dismissed statuses.
- * @param outState the {@link Bundle} provided by Activity.onSaveInstanceState(Bundle).
- */
- public void onSaveInstanceState(final Bundle outState) {
- outState.putLong(EXTRA_ACTIVE_REMOVED_ID, mCurrentRemovedId);
- }
-
- /**
- * This method should be called in your {@link Activity#onRestoreInstanceState(Bundle)} to remember dismissed statuses.
- */
- public void onRestoreInstanceState(final Bundle savedInstanceState) {
- if (savedInstanceState != null) {
- mCurrentRemovedId = savedInstanceState.getLong(EXTRA_ACTIVE_REMOVED_ID, -1);
- }
- }
-
- /**
- * Animate the item at given position away and show the undo {@link View}.
- * @param position the position.
- */
- public void swipeViewAtPosition(final int position) {
- mCurrentRemovedId = getItemId(position);
- for (int i = 0; i < getAbsListView().getChildCount(); i++) {
- AbsListView absListView = getAbsListView();
- View childView = absListView.getChildAt(i);
- int positionForView = AdapterViewUtil.getPositionForView(absListView, childView);
- if (positionForView == position) {
- swipeView(childView, positionForView);
- }
- }
- }
-
- private void swipeView(final View view, final int dismissPosition) {
- ObjectAnimator animator = ObjectAnimator.ofFloat(view, X, view.getMeasuredWidth());
- animator.addListener(new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(final Animator animator) {
- onViewSwiped(((ContextualUndoView) view).getItemId(), dismissPosition);
- }
- });
- animator.start();
- }
-
- @Override
- public void setIsParentHorizontalScrollContainer(final boolean isParentHorizontalScrollContainer) {
- super.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer);
- if (mContextualUndoListViewTouchListener != null) {
- mContextualUndoListViewTouchListener.setIsParentHorizontalScrollContainer(isParentHorizontalScrollContainer);
- }
- }
-
- @Override
- public void setTouchChild(final int childResId) {
- super.setTouchChild(childResId);
- if (mContextualUndoListViewTouchListener != null) {
- mContextualUndoListViewTouchListener.setTouchChild(childResId);
- }
- }
-
- /**
- * Removes any item that was swiped away.
- * @param animate If true, animates the removal (collapsing the item).
- * If false, removes item immediately without animation.
- * @deprecated use {@link #removePendingItem()} or {@link #animateRemovePendingItem()} instead.
- */
- @Deprecated
- public void removePendingItem(final boolean animate) {
- if (animate) {
- animateRemovePendingItem();
- } else {
- removePendingItem();
- }
- }
-
- /**
- * Cancels the count down, and removes any item that was swiped away, without animating. Will cause {@link DeleteItemCallback#deleteItem(int)} to be called.
- */
- public void removePendingItem() {
- if (mCurrentRemovedView != null || mCurrentRemovedId >= 0) {
- new RemoveViewAnimatorListenerAdapter(mCurrentRemovedView, mCurrentRemovedId).onAnimationEnd(null);
- clearCurrentRemovedView();
- }
- }
-
- /**
- * Removes any item that was swiped away, animating the removal (collapsing the item). {@link DeleteItemCallback#deleteItem(int)} to be called.
- */
- public void animateRemovePendingItem() {
- removePreviousContextualUndoIfPresent();
- }
-
- /**
- * Cancel the count down. This will not cause the {@link DeleteItemCallback#deleteItem(int)} to be called. Use {@link #removePendingItem()} for that instead.
- */
- public void cancelCountDown() {
- mHandler.removeCallbacks(mCountDownRunnable);
- }
-
- /**
- * A callback interface which is used to notify when items should be removed from the collection.
- */
- public interface DeleteItemCallback {
- /**
- * Called when an item should be removed from the collection.
- *
- * @param position
- * the position of the item that should be removed.
- */
- public void deleteItem(int position);
- }
-
- /**
- * A callback interface which is used to provide the text to display when counting down.
- */
- public interface CountDownFormatter {
- /**
- * Called each tick of the CountDownTimer
- * @param millisLeft time in milliseconds remaining before the item is automatically removed
- */
- public String getCountDownString(final long millisLeft);
- }
-
- private class CountDownRunnable implements Runnable {
-
- @Override
- public void run() {
- long millisRemaining = mAutoDeleteDelayMillis - (System.currentTimeMillis() - mDismissStartMillis);
- if (mCountDownFormatter != null) {
- mCurrentRemovedView.updateCountDownTimer(mCountDownFormatter.getCountDownString(millisRemaining));
- }
-
- if (millisRemaining <= 0) {
- performRemovalIfNecessary();
- } else {
- mHandler.postDelayed(this, Math.min(millisRemaining, 1000));
- }
- }
- }
-
- private class RemoveViewAnimatorListenerAdapter extends AnimatorListenerAdapter {
-
- private ContextualUndoView mDismissView;
- private final long mDismissViewId;
- private final int mOriginalHeight;
-
- public RemoveViewAnimatorListenerAdapter(final ContextualUndoView dismissView, final long dismissViewId) {
- mDismissView = dismissView;
- mDismissViewId = dismissViewId;
- mOriginalHeight = dismissView.getHeight();
- }
-
- @Override
- public void onAnimationEnd(final Animator animation) {
- mDismissView = getViewBeingAnimated();
- if (mDismissView == null) {
- deleteItemGivenId(mDismissViewId);
- return;
- }
-
- restoreViewPosition(mDismissView);
- restoreViewDimension(mDismissView);
- deleteCurrentItem(mDismissView);
- }
-
- private void restoreViewDimension(final View view) {
- ViewGroup.LayoutParams lp;
- lp = view.getLayoutParams();
- lp.height = mOriginalHeight;
- view.setLayoutParams(lp);
- }
-
- private void deleteCurrentItem(final View view) {
- int position = AdapterViewUtil.getPositionForView(getAbsListView(), view);
- mDeleteItemCallback.deleteItem(position);
- }
-
- private ContextualUndoView getViewBeingAnimated() {
- ContextualUndoView newDismissView = getCurrentRemovedView(mDismissView, mDismissViewId);
- if (newDismissView != mDismissView) {
- restoreViewPosition(mDismissView);
- restoreViewDimension(mDismissView);
-
- mDismissView = newDismissView;
- }
- return mDismissView;
- }
- }
-
- private class RemoveViewAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener {
-
- final RemoveViewAnimatorListenerAdapter mParentAdapter;
- private final ViewGroup.LayoutParams mLayoutParams;
-
- public RemoveViewAnimatorUpdateListener(final RemoveViewAnimatorListenerAdapter parentAdapter) {
- mParentAdapter = parentAdapter;
- mLayoutParams = parentAdapter.mDismissView.getLayoutParams();
- }
-
- @Override
- public void onAnimationUpdate(final ValueAnimator valueAnimator) {
- ContextualUndoView dismissView = mParentAdapter.getViewBeingAnimated();
- if (dismissView != null) {
- mLayoutParams.height = (Integer) valueAnimator.getAnimatedValue();
- dismissView.setLayoutParams(mLayoutParams);
- }
- }
- }
-
- private class UndoListener implements View.OnClickListener {
-
- private final ContextualUndoView mContextualUndoView;
-
- public UndoListener(final ContextualUndoView contextualUndoView) {
- mContextualUndoView = contextualUndoView;
- }
-
- @Override
- public void onClick(final View v) {
- clearCurrentRemovedView();
- mContextualUndoView.displayContentView();
- moveViewOffScreen();
- animateViewComingBack();
- }
-
- private void moveViewOffScreen() {
- ViewHelper.setTranslationX(mContextualUndoView, mContextualUndoView.getWidth());
- }
-
- private void animateViewComingBack() {
- animate(mContextualUndoView).translationX(0).setDuration(ANIMATION_DURATION).setListener(null);
- }
- }
-
- private class HierarchyChangeListener implements ViewGroup.OnHierarchyChangeListener {
- @Override
- public void onChildViewAdded(final View parent, final View child) {
- final ViewHolder vh = ViewHolder.getViewHolder(child);
- if (vh != null && mCurrentRemovedId > 0 && vh.mItemId == mCurrentRemovedId) {
- mCurrentRemovedView = (ContextualUndoView) child;
- }
- }
-
- @Override
- public void onChildViewRemoved(final View parent, final View child) {
- final ViewHolder vh = ViewHolder.getViewHolder(child);
- if (vh != null && mCurrentRemovedId > 0 && vh.mItemId == mCurrentRemovedId) {
- mCurrentRemovedView = null;
- }
- }
- }
-
- private static class ViewHolder {
- final ContextualUndoView mContextualUndoView;
-
- long mItemId;
-
- static ViewHolder getViewHolder(final View view) {
- return (ViewHolder) view.getTag();
- }
-
- ViewHolder(final ContextualUndoView contextualUndoView) {
- mContextualUndoView = contextualUndoView;
- mContextualUndoView.setTag(this);
- }
- }
-}
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoListViewTouchListener.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoListViewTouchListener.java
deleted file mode 100644
index 8a82433b..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoListViewTouchListener.java
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright 2012 Roman Nurik
- * Copyright 2013 Frankie Sardo
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo;
-
-import android.graphics.Rect;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager;
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeOnTouchListener;
-import com.nhaarman.listviewanimations.util.AdapterViewUtil;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorListenerAdapter;
-
-import static com.nineoldandroids.view.ViewHelper.setAlpha;
-import static com.nineoldandroids.view.ViewHelper.setTranslationX;
-import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
-
-/**
- * An {@link OnTouchListener} for the {@link ContextualUndoAdapter}. Don't use
- * this class directly, use ContextualUndoAdapter to wrap your
- * {@link BaseAdapter}s.
- */
-public class ContextualUndoListViewTouchListener implements SwipeOnTouchListener {
- // Cached ViewConfiguration and system-wide constant values
- private final int mSlop;
- private final int mMinFlingVelocity;
- private final int mMaxFlingVelocity;
- private final long mAnimationTime;
-
- // Fixed properties
- private final AbsListView mListView;
- private final Callback mCallback;
- private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
-
- // Transient properties
- private float mDownX;
- private float mDownY;
- private boolean mSwiping;
- private VelocityTracker mVelocityTracker;
- private int mDownPosition;
- private View mDownView;
- private boolean mPaused;
- private boolean mDisallowSwipe;
-
- private boolean mIsParentHorizontalScrollContainer;
- private int mResIdOfTouchChild;
- private boolean mTouchChildTouched;
-
- private DismissableManager mDismissableManager;
-
- public interface Callback {
-
- void onViewSwiped(long dismissViewItemId, int dismissPosition);
-
- void onListScrolled();
- }
-
- public ContextualUndoListViewTouchListener(final AbsListView listView, final Callback callback) {
- ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
- mSlop = vc.getScaledTouchSlop();
- mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
- mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
- mAnimationTime = listView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
- mListView = listView;
- mCallback = callback;
- }
-
- public void setEnabled(final boolean enabled) {
- mPaused = !enabled;
- }
-
- /**
- * Set the {@link com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.DismissableManager} to specify which views can or cannot be swiped.
- * @param dismissableManager null for no restrictions.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setDismissableManager(final DismissableManager dismissableManager) {
- mDismissableManager = dismissableManager;
- }
-
- public AbsListView.OnScrollListener makeScrollListener() {
- return new AbsListView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(final AbsListView absListView, final int scrollState) {
- setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
- if (mPaused) {
- mCallback.onListScrolled();
- }
- if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
- mDisallowSwipe = true;
- }
-
- }
-
- @Override
- public void onScroll(final AbsListView absListView, final int firstVisibleItem, final int visibleItemCount,
- final int totalItemCount) {
- }
- };
- }
-
- @Override
- public boolean onTouch(final View view, final MotionEvent motionEvent) {
- if (mViewWidth < 2) {
- mViewWidth = mListView.getWidth();
- }
-
- boolean result;
- switch (motionEvent.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- result = handleDownEvent(view, motionEvent);
- break;
- case MotionEvent.ACTION_MOVE:
- result = handleMoveEvent(view, motionEvent);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- result = handleUpCancelEvent(view, motionEvent);
- break;
- default:
- result = false;
- }
- return result;
- }
-
- private boolean handleDownEvent(final View view, final MotionEvent motionEvent) {
- mDisallowSwipe = false;
- if (mPaused) {
- return false;
- }
-
- // Find the child view that was touched (perform a hit test)
- Rect rect = new Rect();
- int childCount = mListView.getChildCount();
- int[] listViewCoords = new int[2];
- mListView.getLocationOnScreen(listViewCoords);
- int x = (int) motionEvent.getRawX() - listViewCoords[0];
- int y = (int) motionEvent.getRawY() - listViewCoords[1];
- View child;
- for (int i = 0; i < childCount; i++) {
- child = mListView.getChildAt(i);
- child.getHitRect(rect);
- if (rect.contains(x, y)) {
- mDownView = child;
- break;
- }
- }
-
- if (mDownView != null && mDownView instanceof ContextualUndoView) {
- mDownX = motionEvent.getRawX();
- mDownY = motionEvent.getRawY();
- int downPosition = AdapterViewUtil.getPositionForView(mListView, mDownView);
-
- if (mDismissableManager != null) {
- long downId = mListView.getAdapter().getItemId(downPosition);
- if (!mDismissableManager.isDismissable(downId, downPosition)) {
- /* Cancel, not dismissable */
- return false;
- }
- }
-
- mTouchChildTouched = !mIsParentHorizontalScrollContainer && mResIdOfTouchChild == 0;
-
- if (mResIdOfTouchChild != 0) {
- mIsParentHorizontalScrollContainer = false;
-
- final View childView = mDownView.findViewById(mResIdOfTouchChild);
- if (childView != null) {
- final Rect childRect = getChildViewRect(mListView, childView);
- if (childRect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) {
- mTouchChildTouched = true;
- mListView.requestDisallowInterceptTouchEvent(true);
- }
- }
- }
-
- if (mIsParentHorizontalScrollContainer) {
- // Do it now and don't wait until the user moves more than
- // the slop factor.
- mTouchChildTouched = true;
- mListView.requestDisallowInterceptTouchEvent(true);
- }
-
- mDownY = motionEvent.getRawY();
- mDownPosition = AdapterViewUtil.getPositionForView(mListView, mDownView);
-
- if (mTouchChildTouched) {
- mVelocityTracker = VelocityTracker.obtain();
- mVelocityTracker.addMovement(motionEvent);
- } else {
- mVelocityTracker = null;
- }
- }
- view.onTouchEvent(motionEvent);
- return true;
- }
-
- @SuppressWarnings("UnusedParameters")
- private boolean handleMoveEvent(final View view, final MotionEvent motionEvent) {
- if (mVelocityTracker == null || mPaused) {
- return false;
- }
-
- mVelocityTracker.addMovement(motionEvent);
- float deltaX = motionEvent.getRawX() - mDownX;
- float deltaY = motionEvent.getRawY() - mDownY;
- if (mTouchChildTouched && !mDisallowSwipe && Math.abs(deltaX) > mSlop && Math.abs(deltaX) > Math.abs(deltaY)) {
- mSwiping = true;
- mListView.requestDisallowInterceptTouchEvent(true);
-
- // Cancel ListView's touch (un-highlighting the item)
- MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
- cancelEvent.setAction(MotionEvent.ACTION_CANCEL | motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- mListView.onTouchEvent(cancelEvent);
- cancelEvent.recycle();
- }
-
- if (mSwiping) {
- setTranslationX(mDownView, deltaX);
- //noinspection MagicNumber
- setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
- return true;
- }
- return false;
- }
-
- @SuppressWarnings("UnusedParameters")
- private boolean handleUpCancelEvent(final View view, final MotionEvent motionEvent) {
- mDisallowSwipe = false;
- if (mVelocityTracker == null) {
- return false;
- }
-
- float deltaX = motionEvent.getRawX() - mDownX;
- mVelocityTracker.addMovement(motionEvent);
- mVelocityTracker.computeCurrentVelocity(1000);
- float velocityX = Math.abs(mVelocityTracker.getXVelocity());
- float velocityY = Math.abs(mVelocityTracker.getYVelocity());
- boolean dismiss = false;
- boolean dismissRight = false;
- final float absDeltaX = Math.abs(deltaX);
- if (absDeltaX > mViewWidth / 2) {
- dismiss = true;
- dismissRight = deltaX > 0;
- } else if (mMinFlingVelocity <= velocityX && velocityX <= mMaxFlingVelocity && velocityY < velocityX && absDeltaX > mSlop) {
- dismiss = true;
- dismissRight = mVelocityTracker.getXVelocity() > 0;
- }
- if (dismiss) {
- // dismiss
- final long itemId = ((ContextualUndoView) mDownView).getItemId();
- // before animation ends
- final int downPosition = mDownPosition;
- animate(mDownView).translationX(dismissRight ? mViewWidth : -mViewWidth).alpha(0).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animation) {
- mCallback.onViewSwiped(itemId, downPosition);
- }
- });
- } else {
- // cancel
- animate(mDownView).translationX(0).alpha(1).setDuration(mAnimationTime).setListener(null);
- }
-
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mDownX = 0;
- mDownView = null;
- mDownPosition = AdapterView.INVALID_POSITION;
- mSwiping = false;
- return false;
- }
-
- @Override
- public boolean isSwiping() {
- return mSwiping;
- }
-
- private Rect getChildViewRect(final View parentView, View childView) {
- final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
- if (parentView == childView) {
- return childRect;
-
- }
-
- ViewGroup parent;
- while ((parent = (ViewGroup) childView.getParent()) != parentView) {
- childRect.offset(parent.getLeft(), parent.getTop());
- childView = parent;
- }
-
- return childRect;
- }
-
- void setIsParentHorizontalScrollContainer(final boolean isParentHorizontalScrollContainer) {
- mIsParentHorizontalScrollContainer = mResIdOfTouchChild == 0 && isParentHorizontalScrollContainer;
- }
-
- void setTouchChild(final int childResId) {
- mResIdOfTouchChild = childResId;
- if (childResId != 0) {
- setIsParentHorizontalScrollContainer(false);
- }
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoView.java b/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoView.java
deleted file mode 100644
index 282a6137..00000000
--- a/library/src/com/nhaarman/listviewanimations/itemmanipulation/swipedismiss/contextualundo/ContextualUndoView.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2013 Frankie Sardo
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.itemmanipulation.swipedismiss.contextualundo;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-@SuppressLint("ViewConstructor")
-public class ContextualUndoView extends FrameLayout {
-
- private View mUndoView;
- private View mContentView;
- private TextView mCountDownTV;
-
- private long mItemId;
-
- public ContextualUndoView(final Context context, final int undoLayoutResId, final int countDownTextViewResId) {
- super(context);
- initUndo(undoLayoutResId, countDownTextViewResId);
- }
-
- private void initUndo(final int undoLayoutResId, final int countDownTextViewResId) {
- mUndoView = View.inflate(getContext(), undoLayoutResId, null);
- addView(mUndoView);
-
- if (countDownTextViewResId != -1) {
- mCountDownTV = (TextView) mUndoView.findViewById(countDownTextViewResId);
- }
- }
-
- public void updateCountDownTimer(final String timerText) {
- if (mCountDownTV != null) {
- mCountDownTV.setText(timerText);
- }
- }
-
- public void updateContentView(final View contentView) {
- if (mContentView == null) {
- addView(contentView);
- }
- mContentView = contentView;
- }
-
- public View getContentView() {
- return mContentView;
- }
-
- public void setItemId(final long itemId) {
- this.mItemId = itemId;
- }
-
- public long getItemId() {
- return mItemId;
- }
-
- public boolean isContentDisplayed() {
- return mContentView.getVisibility() == View.VISIBLE;
- }
-
- public void displayUndo() {
- updateCountDownTimer("");
- mContentView.setVisibility(View.INVISIBLE);
- mUndoView.setVisibility(View.VISIBLE);
- }
-
- public void displayContentView() {
- mContentView.setVisibility(View.VISIBLE);
- mUndoView.setVisibility(View.INVISIBLE);
- }
-}
\ No newline at end of file
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/AnimationAdapter.java b/library/src/com/nhaarman/listviewanimations/swinginadapters/AnimationAdapter.java
deleted file mode 100644
index 50925f61..00000000
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/AnimationAdapter.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.swinginadapters;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.GridView;
-
-import com.nhaarman.listviewanimations.BaseAdapterDecorator;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorSet;
-import com.nineoldandroids.animation.ObjectAnimator;
-import com.nineoldandroids.view.ViewHelper;
-
-/**
- * A {@link BaseAdapterDecorator} class which applies multiple {@link Animator}s at once to
- * views when they are first shown. The Animators applied include the animations
- * specified in {@link #getAnimators(ViewGroup, View)}, plus an alpha transition.
- */
-public abstract class AnimationAdapter extends BaseAdapterDecorator {
-
- protected static final long DEFAULTANIMATIONDELAYMILLIS = 100;
- protected static final long DEFAULTANIMATIONDURATIONMILLIS = 300;
- private static final long INITIALDELAYMILLIS = 150;
- private static final String ALPHA = "alpha";
-
- private final SparseArray mAnimators;
- private long mAnimationStartMillis;
- private int mFirstAnimatedPosition;
- private int mLastAnimatedPosition;
- private boolean mHasParentAnimationAdapter;
- private boolean mShouldAnimate = true;
-
- private long mInitialDelayMillis = INITIALDELAYMILLIS;
- private long mAnimationDelayMillis = DEFAULTANIMATIONDELAYMILLIS;
- private long mAnimationDurationMillis = DEFAULTANIMATIONDURATIONMILLIS;
-
- public AnimationAdapter(final BaseAdapter baseAdapter) {
- super(baseAdapter);
- mAnimators = new SparseArray();
-
- mAnimationStartMillis = -1;
- mFirstAnimatedPosition = -1;
- mLastAnimatedPosition = -1;
-
- if (baseAdapter instanceof AnimationAdapter) {
- ((AnimationAdapter) baseAdapter).setHasParentAnimationAdapter(true);
- }
- }
-
- /**
- * Call this method to reset animation status on all views. The next time
- * {@link #notifyDataSetChanged()} is called on the base adapter, all views will
- * animate again. Will also call {@link #setShouldAnimate(boolean)} with a value of true.
- */
- public void reset() {
- mAnimators.clear();
- mFirstAnimatedPosition = -1;
- mLastAnimatedPosition = -1;
- mAnimationStartMillis = -1;
- mShouldAnimate = true;
-
- if (getDecoratedBaseAdapter() instanceof AnimationAdapter) {
- ((AnimationAdapter) getDecoratedBaseAdapter()).reset();
- }
- }
-
- /**
- * Set whether to animate the {@link View}s or not.
- * @param shouldAnimate true if the Views should be animated.
- */
- public void setShouldAnimate(final boolean shouldAnimate) {
- mShouldAnimate = shouldAnimate;
- }
-
- /**
- * Set the starting position for which items should animate. Given position will animate as well.
- * Will also call setShouldAnimate(true).
- * @param position the position.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setShouldAnimateFromPosition(final int position) {
- mShouldAnimate = true;
- mFirstAnimatedPosition = position - 1;
- mLastAnimatedPosition = position - 1;
- }
-
- /**
- * Set the starting position for which items should animate as the first position which isn't currently visible on screen.
- * This call is also valid when the {@link View}s haven't been drawn yet.
- * Will also call setShouldAnimate(true).
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setShouldAnimateNotVisible() {
- if (getAbsListView() == null) {
- throw new IllegalStateException("Call setListView() on this AnimationAdapter before setShouldAnimateNotVisible()!");
- }
-
- mShouldAnimate = true;
- mFirstAnimatedPosition = getAbsListView().getLastVisiblePosition();
- mLastAnimatedPosition = getAbsListView().getLastVisiblePosition();
- }
-
- @Override
- public final View getView(final int position, final View convertView, final ViewGroup parent) {
- if (!mHasParentAnimationAdapter) {
- if (getAbsListView() == null) {
- throw new IllegalStateException("Call setListView() on this AnimationAdapter before setAdapter()!");
- }
-
- if (convertView != null) {
- cancelExistingAnimation(convertView);
- }
- }
-
- View itemView = super.getView(position, convertView, parent);
-
- if (!mHasParentAnimationAdapter) {
- animateViewIfNecessary(position, itemView, parent);
- }
- return itemView;
- }
-
- private void cancelExistingAnimation(final View convertView) {
- int hashCode = convertView.hashCode();
- Animator animator = mAnimators.get(hashCode);
- if (animator != null) {
- animator.end();
- mAnimators.remove(hashCode);
- }
- }
-
- private void animateViewIfNecessary(final int position, final View view, final ViewGroup parent) {
- boolean isMeasuringGridViewItem = getAbsListView() instanceof GridView && parent.getHeight() == 0;
-
- if (position > mLastAnimatedPosition && mShouldAnimate && !isMeasuringGridViewItem) {
- if (mFirstAnimatedPosition == -1) {
- mFirstAnimatedPosition = position;
- }
-
- animateView(parent, view);
- mLastAnimatedPosition = position;
- }
- }
-
- private void animateView(final ViewGroup parent, final View view) {
- if (mAnimationStartMillis == -1) {
- mAnimationStartMillis = System.currentTimeMillis();
- }
-
- ViewHelper.setAlpha(view, 0);
-
- Animator[] childAnimators;
- if (mDecoratedBaseAdapter instanceof AnimationAdapter) {
- childAnimators = ((AnimationAdapter) mDecoratedBaseAdapter).getAnimators(parent, view);
- } else {
- childAnimators = new Animator[0];
- }
- Animator[] animators = getAnimators(parent, view);
- Animator alphaAnimator = ObjectAnimator.ofFloat(view, ALPHA, 0, 1);
-
- AnimatorSet set = new AnimatorSet();
- set.playTogether(concatAnimators(childAnimators, animators, alphaAnimator));
- set.setStartDelay(calculateAnimationDelay());
- set.setDuration(getAnimationDurationMillis());
- set.start();
-
- mAnimators.put(view.hashCode(), set);
- }
-
- private Animator[] concatAnimators(final Animator[] childAnimators, final Animator[] animators, final Animator alphaAnimator) {
- Animator[] allAnimators = new Animator[childAnimators.length + animators.length + 1];
- int i;
-
- for (i = 0; i < animators.length; ++i) {
- allAnimators[i] = animators[i];
- }
-
- for (Animator childAnimator : childAnimators) {
- allAnimators[i] = childAnimator;
- ++i;
- }
-
- allAnimators[allAnimators.length - 1] = alphaAnimator;
- return allAnimators;
- }
-
- @SuppressLint("NewApi")
- private long calculateAnimationDelay() {
- long delay;
-
- int lastVisiblePosition = getAbsListView().getLastVisiblePosition();
- int firstVisiblePosition = getAbsListView().getFirstVisiblePosition();
-
- int numberOfItemsOnScreen = lastVisiblePosition - firstVisiblePosition;
- int numberOfAnimatedItems = mLastAnimatedPosition - mFirstAnimatedPosition;
-
- if (numberOfItemsOnScreen + 1 < numberOfAnimatedItems) {
- delay = getAnimationDelayMillis();
-
- if (getAbsListView() instanceof GridView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- delay += getAnimationDelayMillis() * ((mLastAnimatedPosition + 1) % ((GridView) getAbsListView()).getNumColumns());
- }
- } else {
- long delaySinceStart = (mLastAnimatedPosition - mFirstAnimatedPosition + 1) * getAnimationDelayMillis();
- delay = mAnimationStartMillis + getInitialDelayMillis() + delaySinceStart - System.currentTimeMillis();
- }
- return Math.max(0, delay);
- }
-
- /**
- * Set whether this AnimationAdapter is encapsulated by another
- * AnimationAdapter. When this is set to true, this AnimationAdapter does
- * not apply any animations to the views. Should not be set explicitly, the
- * AnimationAdapter class manages this by itself.
- */
- public void setHasParentAnimationAdapter(final boolean hasParentAnimationAdapter) {
- mHasParentAnimationAdapter = hasParentAnimationAdapter;
- }
-
- /**
- * Get the delay in milliseconds before the first animation should start. Defaults to {@value #INITIALDELAYMILLIS}.
- */
- protected long getInitialDelayMillis() {
- return mInitialDelayMillis;
- }
-
- /**
- * Set the delay in milliseconds before the first animation should start. Defaults to {@value #INITIALDELAYMILLIS}.
- * @param delayMillis the time in milliseconds.
- */
- public void setInitialDelayMillis(final long delayMillis) {
- mInitialDelayMillis = delayMillis;
- }
-
- /**
- * Get the delay in milliseconds before an animation of a view should start. Defaults to {@value #DEFAULTANIMATIONDELAYMILLIS}.
- */
- protected long getAnimationDelayMillis() {
- return mAnimationDelayMillis;
- }
-
- /**
- * Set the delay in milliseconds before an animation of a view should start. Defaults to {@value #DEFAULTANIMATIONDELAYMILLIS}.
- * @param delayMillis the time in milliseconds.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setAnimationDelayMillis(final long delayMillis) {
- mAnimationDelayMillis = delayMillis;
- }
-
- /**
- * Get the duration of the animation in milliseconds. Defaults to {@value #DEFAULTANIMATIONDURATIONMILLIS}.
- */
- protected long getAnimationDurationMillis() {
- return mAnimationDurationMillis;
- }
-
- /**
- * Set the duration of the animation in milliseconds. Defaults to {@value #DEFAULTANIMATIONDURATIONMILLIS}.
- * @param durationMillis the time in milliseconds.
- */
- @SuppressWarnings("UnusedDeclaration")
- public void setAnimationDurationMillis(final long durationMillis) {
- mAnimationDurationMillis = durationMillis;
- }
-
- /**
- * Get the Animators to apply to the views. In addition to the returned
- * Animators, an alpha transition will be applied to the view.
- *
- * @param parent
- * The parent of the view
- * @param view
- * The view that will be animated, as retrieved by getView()
- */
- public abstract Animator[] getAnimators(ViewGroup parent, View view);
-}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/AlphaInAnimationAdapter.java b/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/AlphaInAnimationAdapter.java
deleted file mode 100644
index dac9feb4..00000000
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/AlphaInAnimationAdapter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.nhaarman.listviewanimations.swinginadapters.prepared;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.swinginadapters.AnimationAdapter;
-import com.nineoldandroids.animation.Animator;
-
-public class AlphaInAnimationAdapter extends AnimationAdapter {
-
- public AlphaInAnimationAdapter(final BaseAdapter baseAdapter) {
- super(baseAdapter);
- }
-
- @Override
- protected long getAnimationDelayMillis() {
- return DEFAULTANIMATIONDELAYMILLIS;
- }
-
- @Override
- protected long getAnimationDurationMillis() {
- return DEFAULTANIMATIONDURATIONMILLIS;
- }
-
- @Override
- public Animator[] getAnimators(final ViewGroup parent, final View view) {
- return new Animator[0];
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingBottomInAnimationAdapter.java b/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingBottomInAnimationAdapter.java
deleted file mode 100644
index 8aeecc2d..00000000
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingBottomInAnimationAdapter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.swinginadapters.prepared;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.swinginadapters.SingleAnimationAdapter;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.ObjectAnimator;
-
-/**
- * An implementation of the AnimationAdapter class which applies a
- * swing-in-from-bottom-animation to views.
- */
-public class SwingBottomInAnimationAdapter extends SingleAnimationAdapter {
-
- private static final String TRANSLATION_Y = "translationY";
- private final long mAnimationDelayMillis;
- private final long mAnimationDurationMillis;
-
- public SwingBottomInAnimationAdapter(final BaseAdapter baseAdapter) {
- this(baseAdapter, DEFAULTANIMATIONDELAYMILLIS, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingBottomInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis) {
- this(baseAdapter, animationDelayMillis, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingBottomInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis, final long animationDurationMillis) {
- super(baseAdapter);
- mAnimationDelayMillis = animationDelayMillis;
- mAnimationDurationMillis = animationDurationMillis;
- }
-
- @Override
- protected long getAnimationDelayMillis() {
- return mAnimationDelayMillis;
- }
-
- @Override
- protected long getAnimationDurationMillis() {
- return mAnimationDurationMillis;
- }
-
- @Override
- protected Animator getAnimator(final ViewGroup parent, final View view) {
- // TODO magic number
- return ObjectAnimator.ofFloat(view, TRANSLATION_Y, 500, 0);
- }
-
-}
diff --git a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingRightInAnimationAdapter.java b/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingRightInAnimationAdapter.java
deleted file mode 100644
index 1e3d00e1..00000000
--- a/library/src/com/nhaarman/listviewanimations/swinginadapters/prepared/SwingRightInAnimationAdapter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.swinginadapters.prepared;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-import com.nhaarman.listviewanimations.swinginadapters.SingleAnimationAdapter;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.ObjectAnimator;
-
-/**
- * An implementation of the AnimationAdapter class which applies a
- * swing-in-from-the-right-animation to views.
- */
-public class SwingRightInAnimationAdapter extends SingleAnimationAdapter {
-
- private static final String TRANSLATION_X = "translationX";
- private final long mAnimationDelayMillis;
- private final long mAnimationDurationMillis;
-
- public SwingRightInAnimationAdapter(final BaseAdapter baseAdapter) {
- this(baseAdapter, DEFAULTANIMATIONDELAYMILLIS, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingRightInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis) {
- this(baseAdapter, animationDelayMillis, DEFAULTANIMATIONDURATIONMILLIS);
- }
-
- public SwingRightInAnimationAdapter(final BaseAdapter baseAdapter, final long animationDelayMillis, final long animationDurationMillis) {
- super(baseAdapter);
- mAnimationDelayMillis = animationDelayMillis;
- mAnimationDurationMillis = animationDurationMillis;
- }
-
- @Override
- protected long getAnimationDelayMillis() {
- return mAnimationDelayMillis;
- }
-
- @Override
- protected long getAnimationDurationMillis() {
- return mAnimationDurationMillis;
- }
-
- @Override
- protected Animator getAnimator(final ViewGroup parent, final View view) {
- return ObjectAnimator.ofFloat(view, TRANSLATION_X, parent.getWidth(), 0);
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/util/AdapterViewUtil.java b/library/src/com/nhaarman/listviewanimations/util/AdapterViewUtil.java
deleted file mode 100644
index 5b373e68..00000000
--- a/library/src/com/nhaarman/listviewanimations/util/AdapterViewUtil.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.util;
-
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ListView;
-
-public class AdapterViewUtil {
-
- /**
- * Get the position within the adapter's dataset for the view, where view is an adapter item or a descendant of an adapter item.
- * Unlike {@link AdapterView#getPositionForView(android.view.View)}, returned position will reflect the position of the item given view is representing,
- * by subtracting the header views count.
- * @param adapterView the AdapterView containing the view.
- * @param view an adapter item or a descendant of an adapter item. This must be visible in given AdapterView at the time of the call.
- * @return the position of the item in the AdapterView represented by given view, or {@link AdapterView#INVALID_POSITION} if the view does not
- * correspond to a list item (or it is not visible).
- */
- public static int getPositionForView(final AdapterView> adapterView, final View view) {
- int position = adapterView.getPositionForView(view);
-
- if (adapterView instanceof ListView) {
- position -= ((ListView) adapterView).getHeaderViewsCount();
- }
-
- return position;
- }
-}
diff --git a/library/src/com/nhaarman/listviewanimations/widget/DynamicListView.java b/library/src/com/nhaarman/listviewanimations/widget/DynamicListView.java
deleted file mode 100644
index b45c8f79..00000000
--- a/library/src/com/nhaarman/listviewanimations/widget/DynamicListView.java
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- * Copyright 2013 Niek Haarman
- *
- * 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.nhaarman.listviewanimations.widget;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.HeaderViewListAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-import com.nhaarman.listviewanimations.itemmanipulation.swipedismiss.SwipeOnTouchListener;
-import com.nineoldandroids.animation.Animator;
-import com.nineoldandroids.animation.AnimatorListenerAdapter;
-import com.nineoldandroids.animation.ObjectAnimator;
-import com.nineoldandroids.animation.TypeEvaluator;
-import com.nineoldandroids.animation.ValueAnimator;
-import com.nineoldandroids.view.ViewHelper;
-
-/**
- * The DynamicListView is an extension of {@link ListView} that supports cell dragging
- * and swapping.
- *
- * Make sure your adapter has stable ids, and override {@link ListAdapter#hasStableIds()} to return true.
- *
- * This layout is in charge of positioning the hover cell in the correct location
- * on the screen in response to user touch events. It uses the position of the
- * hover cell to determine when two cells should be swapped. If two cells should
- * be swapped, all the corresponding data set and layout changes are handled here.
- *
- * If no cell is selected, all the touch events are passed down to the ListView
- * and behave normally. If one of the items in the ListView experiences a
- * long press event, the contents of its current visible state are captured as
- * a bitmap and its visibility is set to INVISIBLE. A hover cell is then created and
- * added to this layout as an overlaying BitmapDrawable above the ListView. Once the
- * hover cell is translated some distance to signify an item swap, a data set change
- * accompanied by animation takes place. When the user releases the hover cell,
- * it animates into its corresponding position in the ListView.
- *
- * When the hover cell is either above or below the bounds of the ListView, this
- * ListView also scrolls on its own so as to reveal additional content.
- *
- * See http://youtu.be/_BZIvjMgH-Q
- */
-public class DynamicListView extends ListView {
-
- private int mOriginalTranscriptMode;
-
- public interface OnHoverCellListener {
- public Drawable onHoverCellCreated(Drawable hoverCellDrawable);
- }
-
- /**
- * Implement this interface to be notified of ordering changes. Call {@link #setOnItemMovedListener(com.nhaarman.listviewanimations.widget.DynamicListView.OnItemMovedListener)}.
- */
- public interface OnItemMovedListener {
- /**
- * Called after an item is dropped and moved.
- *
- * @param newPosition the new position of the item.
- */
- public void onItemMoved(int newPosition);
- }
-
- private final int SMOOTH_SCROLL_AMOUNT_AT_EDGE = 15;
- private final int MOVE_DURATION = 150;
-
- private int mLastEventY = -1, mLastEventX = -1;
-
- private int mDownY = -1;
- private int mDownX = -1;
-
- private int mTotalOffset = 0;
-
- private boolean mCellIsMobile = false;
- private boolean mIsMobileScrolling = false;
- private int mSmoothScrollAmountAtEdge = 0;
-
- private final int INVALID_ID = -1;
- private long mAboveItemId = INVALID_ID;
- private long mMobileItemId = INVALID_ID;
- private long mBelowItemId = INVALID_ID;
-
- private Drawable mHoverCell;
- private Rect mHoverCellCurrentBounds;
- private Rect mHoverCellOriginalBounds;
-
- private final int INVALID_POINTER_ID = -1;
- private int mActivePointerId = INVALID_POINTER_ID;
-
- private boolean mIsWaitingForScrollFinish = false;
- private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
- private OnTouchListener mOnTouchListener;
- private boolean mIsParentHorizontalScrollContainer;
- private int mResIdOfDynamicTouchChild;
- private boolean mDynamicTouchChildTouched;
- private int mSlop;
-
- private boolean mSkipCallingOnTouchListener;
-
- private OnHoverCellListener mOnHoverCellListener;
-
- private OnItemMovedListener mOnItemMovedListener;
- private int mLastMovedToIndex;
-
- public DynamicListView(Context context) {
- super(context);
- init(context);
- }
-
- public DynamicListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context);
- }
-
- public DynamicListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public void init(Context context) {
- setOnItemLongClickListener(mOnItemLongClickListener);
- setOnScrollListener(mScrollListener);
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- mSmoothScrollAmountAtEdge = (int) (SMOOTH_SCROLL_AMOUNT_AT_EDGE / metrics.density);
- ViewConfiguration vc = ViewConfiguration.get(getContext());
- mSlop = vc.getScaledTouchSlop();
- }
-
- public void setAdapter(BaseAdapter adapter) {
- super.setAdapter(adapter);
- }
-
- @Override
- @Deprecated
- /**
- * @deprecated use #setAdapter(BaseAdapter) instead.
- */
- public void setAdapter(ListAdapter adapter) {
- if (!(adapter instanceof BaseAdapter)) {
- throw new IllegalArgumentException("DynamicListView needs a BaseAdapter!");
- }
- super.setAdapter(adapter);
- }
-
- /**
- * Listens for long clicks on any items in the listview. When a cell has
- * been selected, the hover cell is created and set up.
- */
- private OnItemLongClickListener mOnItemLongClickListener = new OnItemLongClickListener() {
- public boolean onItemLongClick(AdapterView> arg0, View arg1, int pos, long id) {
- if (mResIdOfDynamicTouchChild == 0) {
- mDynamicTouchChildTouched = true;
- makeCellMobile();
- return true;
- }
- return false;
- }
- };
-
- private void makeCellMobile() {
- int position = pointToPosition(mDownX, mDownY);
- int itemNum = position - getFirstVisiblePosition();
- View selectedView = getChildAt(itemNum);
- if (selectedView == null || position < getHeaderViewsCount() || position >= getAdapter().getCount() - getHeaderViewsCount()) {
- return;
- }
-
- mOriginalTranscriptMode = getTranscriptMode();
- setTranscriptMode(TRANSCRIPT_MODE_NORMAL);
-
-
- mTotalOffset = 0;
-
- mMobileItemId = getAdapter().getItemId(position);
- mHoverCell = getAndAddHoverView(selectedView);
- if (mOnHoverCellListener != null) {
- mHoverCell = mOnHoverCellListener.onHoverCellCreated(mHoverCell);
- }
- selectedView.setVisibility(INVISIBLE);
-
- mCellIsMobile = true;
- getParent().requestDisallowInterceptTouchEvent(true);
-
- updateNeighborViewsForId(mMobileItemId);
- }
-
- /**
- * Creates the hover cell with the appropriate bitmap and of appropriate
- * size. The hover cell's BitmapDrawable is drawn on top of the bitmap every
- * single time an invalidate call is made.
- */
- private BitmapDrawable getAndAddHoverView(View v) {
- int w = v.getWidth();
- int h = v.getHeight();
- int top = v.getTop();
- int left = v.getLeft();
-
- Bitmap b = getBitmapFromView(v);
-
- BitmapDrawable drawable = new BitmapDrawable(getResources(), b);
-
- mHoverCellOriginalBounds = new Rect(left, top, left + w, top + h);
- mHoverCellCurrentBounds = new Rect(mHoverCellOriginalBounds);
-
- drawable.setBounds(mHoverCellCurrentBounds);
-
- return drawable;
- }
-
- /**
- * Returns a bitmap showing a screenshot of the view passed in.
- */
- private Bitmap getBitmapFromView(View v) {
- Bitmap bitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- v.draw(canvas);
- return bitmap;
- }
-
- /**
- * Stores a reference to the views above and below the item currently
- * corresponding to the hover cell. It is important to note that if this
- * item is either at the top or bottom of the list, mAboveItemId or mBelowItemId
- * may be invalid.
- */
- private void updateNeighborViewsForId(long itemId) {
- int position = getPositionForId(itemId);
- ListAdapter adapter = getAdapter();
- if (!adapter.hasStableIds()) {
- throw new IllegalStateException("Adapter doesn't have stable ids! Make sure your adapter has stable ids, and override hasStableIds() to return true.");
- }
-
- mAboveItemId = position - 1 >= 0 ? adapter.getItemId(position - 1) : INVALID_ROW_ID;
- mBelowItemId = position + 1 < adapter.getCount() ? adapter.getItemId(position + 1) : INVALID_ROW_ID;
- }
-
- /**
- * Retrieves the view in the list corresponding to itemId
- */
- private View getViewForId(long itemId) {
- int firstVisiblePosition = getFirstVisiblePosition();
- ListAdapter adapter = getAdapter();
- if (!adapter.hasStableIds()) {
- throw new IllegalStateException("Adapter doesn't have stable ids! Make sure your adapter has stable ids, and override hasStableIds() to return true.");
- }
-
- for (int i = 0; i < getChildCount(); i++) {
- View v = getChildAt(i);
- int position = firstVisiblePosition + i;
- long id = adapter.getItemId(position);
- if (id == itemId) {
- return v;
- }
- }
- return null;
- }
-
- /**
- * Retrieves the position in the list corresponding to itemId
- */
- private int getPositionForId(long itemId) {
- View v = getViewForId(itemId);
- if (v == null) {
- return -1;
- } else {
- return getPositionForView(v);
- }
- }
-
- /**
- * dispatchDraw gets invoked when all the child views are about to be drawn.
- * By overriding this method, the hover cell (BitmapDrawable) can be drawn
- * over the listview's items whenever the listview is redrawn.
- */
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mHoverCell != null) {
- mHoverCell.draw(canvas);
- }
- }
-
- @Override
- public void setOnTouchListener(OnTouchListener l) {
- mOnTouchListener = l;
- }
-
- public void setOnHoverCellListener(OnHoverCellListener onHoverCellListener) {
- mOnHoverCellListener = onHoverCellListener;
- }
-
- private Rect getChildViewRect(View parentView, View childView) {
- final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom());
- if (parentView == childView) {
- return childRect;
- }
-
- ViewGroup parent;
- while ((parent = (ViewGroup) childView.getParent()) != parentView) {
- childRect.offset(parent.getLeft(), parent.getTop());
- childView = parent;
- }
-
- return childRect;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mSkipCallingOnTouchListener) {
- return super.onTouchEvent(event);
- }
-
- if (mOnTouchListener instanceof SwipeOnTouchListener) {
- if (((SwipeOnTouchListener) mOnTouchListener).isSwiping()) {
- mSkipCallingOnTouchListener = true;
- boolean retVal = mOnTouchListener.onTouch(this, event);
- mSkipCallingOnTouchListener = false;
- return retVal || super.onTouchEvent(event);
- }
- }
-
- switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- mDownX = (int) event.getX();
- mDownY = (int) event.getY();
- mActivePointerId = event.getPointerId(0);
-
- mDynamicTouchChildTouched = false;
- if (mResIdOfDynamicTouchChild != 0) {
- mIsParentHorizontalScrollContainer = false;
-
- int position = pointToPosition(mDownX, mDownY);
- int childNum = (position != INVALID_POSITION) ? position - getFirstVisiblePosition() : -1;
- View itemView = (childNum >= 0) ? getChildAt(childNum) : null;
- View childView = (itemView != null) ? itemView.findViewById(mResIdOfDynamicTouchChild) : null;
- if (childView != null) {
- final Rect childRect = getChildViewRect(this, childView);
- if (childRect.contains(mDownX, mDownY)) {
- mDynamicTouchChildTouched = true;
- getParent().requestDisallowInterceptTouchEvent(true);
- }
- }
- }
-
- if (mIsParentHorizontalScrollContainer) {
- // Do it now and don't wait until the user moves more than the
- // slop factor.
- getParent().requestDisallowInterceptTouchEvent(true);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mActivePointerId == INVALID_POINTER_ID) {
- break;
- }
-
- int pointerIndex = event.findPointerIndex(mActivePointerId);
-
- mLastEventY = (int) event.getY(pointerIndex);
- mLastEventX = (int) event.getX(pointerIndex);
- int deltaY = mLastEventY - mDownY;
- int deltaX = mLastEventX - mDownX;
-
- if (!mCellIsMobile && mDynamicTouchChildTouched) {
- if (Math.abs(deltaY) > mSlop && Math.abs(deltaY) > Math.abs(deltaX)) {
- makeCellMobile();
-
- // Cancel ListView's touch (un-highlighting the item)
- MotionEvent cancelEvent = MotionEvent.obtain(event);
- cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
- super.onTouchEvent(cancelEvent);
- cancelEvent.recycle();
- }
- }
-
- if (mCellIsMobile) {
- mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mHoverCellOriginalBounds.top + deltaY + mTotalOffset);
- mHoverCell.setBounds(mHoverCellCurrentBounds);
- invalidate();
-
- handleCellSwitch();
-
- mIsMobileScrolling = false;
- handleMobileCellScroll();
- }
- break;
- case MotionEvent.ACTION_UP:
- mDynamicTouchChildTouched = false;
- touchEventsEnded();
- break;
- case MotionEvent.ACTION_CANCEL:
- mDynamicTouchChildTouched = false;
- touchEventsCancelled();
- break;
- case MotionEvent.ACTION_POINTER_UP:
- /*
- * If a multitouch event took place and the original touch dictating
- * the movement of the hover cell has ended, then the dragging event
- * ends and the hover cell is animated to its corresponding position
- * in the listview.
- */
- pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int pointerId = event.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- mDynamicTouchChildTouched = false;
- touchEventsEnded();
- }
- break;
- default:
- break;
- }
-
- if (mCellIsMobile) {
- return false;
- } else if (mOnTouchListener != null) {
- mSkipCallingOnTouchListener = true;
- boolean retVal = mOnTouchListener.onTouch(this, event);
- mSkipCallingOnTouchListener = false;
- if (retVal) {
- return true;
- }
- }
- return super.onTouchEvent(event);
- }
-
- /**
- * This method determines whether the hover cell has been shifted far enough
- * to invoke a cell swap. If so, then the respective cell swap candidate is
- * determined and the data set is changed. Upon posting a notification of the
- * data set change, a layout is invoked to place the cells in the right place.
- * Using a ViewTreeObserver and a corresponding OnPreDrawListener, we can
- * offset the cell being swapped to where it previously was and then animate it to
- * its new position.
- */
- private void handleCellSwitch() {
- final int deltaY = mLastEventY - mDownY;
- int deltaYTotal = mHoverCellOriginalBounds.top + mTotalOffset + deltaY;
-
- View belowView = getViewForId(mBelowItemId);
- View mobileView = getViewForId(mMobileItemId);
- View aboveView = getViewForId(mAboveItemId);
-
- boolean isBelow = (belowView != null) && (deltaYTotal > belowView.getTop());
- boolean isAbove = (aboveView != null) && (deltaYTotal < aboveView.getTop());
-
- if (isBelow || isAbove) {
-
- final long switchItemId = isBelow ? mBelowItemId : mAboveItemId;
- View switchView = isBelow ? belowView : aboveView;
- final int originalItem = getPositionForView(mobileView);
-
- if (switchView == null) {
- updateNeighborViewsForId(mMobileItemId);
- return;
- }
-
- if (getPositionForView(switchView) < getHeaderViewsCount()) {
- return;
- }
- swapElements(originalItem, getPositionForView(switchView));
-
- BaseAdapter adapter;
- if (getAdapter() instanceof HeaderViewListAdapter) {
- adapter = (BaseAdapter) ((HeaderViewListAdapter) getAdapter()).getWrappedAdapter();
- } else {
- adapter = (BaseAdapter) getAdapter();
- }
- adapter.notifyDataSetChanged();
-
- mDownY = mLastEventY;
- mDownX = mLastEventX;
-
- final int switchViewStartTop = switchView.getTop();
-
- mobileView.setVisibility(View.VISIBLE);
- switchView.setVisibility(View.INVISIBLE);
-
- updateNeighborViewsForId(mMobileItemId);
-
- final ViewTreeObserver observer = getViewTreeObserver();
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- public boolean onPreDraw() {
- observer.removeOnPreDrawListener(this);
-
- View switchView = getViewForId(switchItemId);
-
- mTotalOffset += deltaY;
-
- int switchViewNewTop = switchView.getTop();
- int delta = switchViewStartTop - switchViewNewTop;
-
- ViewHelper.setTranslationY(switchView, delta);
-
- ObjectAnimator animator = ObjectAnimator.ofFloat(switchView, "translationY", 0);
- animator.setDuration(MOVE_DURATION);
- animator.start();
-
- return true;
- }
- });
- }
- }
-
- private void swapElements(int indexOne, int indexTwo) {
- mLastMovedToIndex = indexTwo;
- ListAdapter adapter = getAdapter();
-
- if (adapter instanceof HeaderViewListAdapter) {
- adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
- }
-
- if (adapter instanceof Swappable) {
- ((Swappable) adapter).swapItems(indexOne - getHeaderViewsCount(), indexTwo - getHeaderViewsCount());
- }
- }
-
- /**
- * Resets all the appropriate fields to a default state while also animating
- * the hover cell back to its correct location.
- */
- private void touchEventsEnded() {
- final View mobileView = getViewForId(mMobileItemId);
- if (mCellIsMobile || mIsWaitingForScrollFinish) {
- mCellIsMobile = false;
- mIsWaitingForScrollFinish = false;
- mIsMobileScrolling = false;
- mActivePointerId = INVALID_POINTER_ID;
-
- /* Restore the transcript mode */
- setTranscriptMode(mOriginalTranscriptMode);
-
- // If the autoscroller has not completed scrolling, we need to wait
- // for it to
- // finish in order to determine the final location of where the
- // hover cell
- // should be animated to.
- if (mScrollState != OnScrollListener.SCROLL_STATE_IDLE) {
- mIsWaitingForScrollFinish = true;
- return;
- }
-
- mHoverCellCurrentBounds.offsetTo(mHoverCellOriginalBounds.left, mobileView.getTop());
-
- ObjectAnimator hoverViewAnimator = ObjectAnimator.ofObject(mHoverCell, "bounds", sBoundEvaluator, mHoverCellCurrentBounds);
- hoverViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- invalidate();
- }
- });
- hoverViewAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- setEnabled(false);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAboveItemId = INVALID_ID;
- mMobileItemId = INVALID_ID;
- mBelowItemId = INVALID_ID;
- mobileView.setVisibility(VISIBLE);
- mHoverCell = null;
- setEnabled(true);
- invalidate();
- if (mOnItemMovedListener != null) {
- mOnItemMovedListener.onItemMoved(mLastMovedToIndex - getHeaderViewsCount());
- }
- }
- });
- hoverViewAnimator.start();
- } else {
- touchEventsCancelled();
- }
- }
-
- /**
- * Resets all the appropriate fields to a default state.
- */
- private void touchEventsCancelled() {
- View mobileView = getViewForId(mMobileItemId);
- if (mCellIsMobile) {
- mAboveItemId = INVALID_ID;
- mMobileItemId = INVALID_ID;
- mBelowItemId = INVALID_ID;
- mobileView.setVisibility(VISIBLE);
- mHoverCell = null;
- invalidate();
- }
- mCellIsMobile = false;
- mIsMobileScrolling = false;
- mActivePointerId = INVALID_POINTER_ID;
- }
-
- /**
- * This TypeEvaluator is used to animate the BitmapDrawable back to its
- * final location when the user lifts his finger by modifying the
- * BitmapDrawable's bounds.
- */
- private final static TypeEvaluator sBoundEvaluator = new TypeEvaluator() {
- public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
- return new Rect(interpolate(startValue.left, endValue.left, fraction), interpolate(startValue.top, endValue.top, fraction), interpolate(startValue.right, endValue.right, fraction),
- interpolate(startValue.bottom, endValue.bottom, fraction));
- }
-
- public int interpolate(int start, int end, float fraction) {
- return (int) (start + fraction * (end - start));
- }
- };
-
- /**
- * Determines whether this listview is in a scrolling state invoked
- * by the fact that the hover cell is out of the bounds of the listview;
- */
- private void handleMobileCellScroll() {
- mIsMobileScrolling = handleMobileCellScroll(mHoverCellCurrentBounds);
- }
-
- /**
- * This method is in charge of determining if the hover cell is above
- * or below the bounds of the listview. If so, the listview does an appropriate
- * upward or downward smooth scroll so as to reveal new items.
- */
- private boolean handleMobileCellScroll(Rect r) {
- int offset = computeVerticalScrollOffset();
- int height = getHeight();
- int extent = computeVerticalScrollExtent();
- int range = computeVerticalScrollRange();
- int hoverViewTop = r.top;
- int hoverHeight = r.height();
-
- if (hoverViewTop <= 0 && offset > 0) {
- smoothScrollBy(-mSmoothScrollAmountAtEdge, 0);
- return true;
- }
-
- if (hoverViewTop + hoverHeight >= height && (offset + extent) < range) {
- smoothScrollBy(mSmoothScrollAmountAtEdge, 0);
- return true;
- }
-
- return false;
- }
-
- public void setIsParentHorizontalScrollContainer(boolean isParentHorizontalScrollContainer) {
- mIsParentHorizontalScrollContainer = (mResIdOfDynamicTouchChild == 0) && isParentHorizontalScrollContainer;
- }
-
- public boolean isParentHorizontalScrollContainer() {
- return mIsParentHorizontalScrollContainer;
- }
-
- public void setDynamicTouchChild(int childResId) {
- mResIdOfDynamicTouchChild = childResId;
- if (childResId != 0) {
- setIsParentHorizontalScrollContainer(false);
- }
- }
-
- /**
- * This scroll listener is added to the listview in order to handle cell swapping
- * when the cell is either at the top or bottom edge of the listview. If the hover
- * cell is at either edge of the listview, the listview will begin scrolling. As
- * scrolling takes place, the listview continuously checks if new cells became visible
- * and determines whether they are potential candidates for a cell swap.
- */
- private OnScrollListener mScrollListener = new OnScrollListener() {
-
- private int mPreviousFirstVisibleItem = -1;
- private int mPreviousVisibleItemCount = -1;
- private int mCurrentFirstVisibleItem;
- private int mCurrentVisibleItemCount;
- private int mCurrentScrollState;
-
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
- mCurrentFirstVisibleItem = firstVisibleItem;
- mCurrentVisibleItemCount = visibleItemCount;
-
- mPreviousFirstVisibleItem = (mPreviousFirstVisibleItem == -1) ? mCurrentFirstVisibleItem : mPreviousFirstVisibleItem;
- mPreviousVisibleItemCount = (mPreviousVisibleItemCount == -1) ? mCurrentVisibleItemCount : mPreviousVisibleItemCount;
-
- checkAndHandleFirstVisibleCellChange();
- checkAndHandleLastVisibleCellChange();
-
- mPreviousFirstVisibleItem = mCurrentFirstVisibleItem;
- mPreviousVisibleItemCount = mCurrentVisibleItemCount;
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- mCurrentScrollState = scrollState;
- mScrollState = scrollState;
- isScrollCompleted();
- }
-
- /**
- * This method is in charge of invoking 1 of 2 actions. Firstly, if the listview
- * is in a state of scrolling invoked by the hover cell being outside the bounds
- * of the listview, then this scrolling event is continued. Secondly, if the hover
- * cell has already been released, this invokes the animation for the hover cell
- * to return to its correct position after the listview has entered an idle scroll
- * state.
- */
- private void isScrollCompleted() {
- if (mCurrentVisibleItemCount > 0 && mCurrentScrollState == SCROLL_STATE_IDLE) {
- if (mCellIsMobile && mIsMobileScrolling) {
- handleMobileCellScroll();
- } else if (mIsWaitingForScrollFinish) {
- touchEventsEnded();
- }
- }
- }
-
- /**
- * Determines if the listview scrolled up enough to reveal a new cell at the
- * top of the list. If so, then the appropriate parameters are updated.
- */
- public void checkAndHandleFirstVisibleCellChange() {
- if (mCurrentFirstVisibleItem != mPreviousFirstVisibleItem) {
- if (mCellIsMobile && mMobileItemId != INVALID_ID) {
- updateNeighborViewsForId(mMobileItemId);
- handleCellSwitch();
- }
- }
- }
-
- /**
- * Determines if the listview scrolled down enough to reveal a new cell at the
- * bottom of the list. If so, then the appropriate parameters are updated.
- */
- public void checkAndHandleLastVisibleCellChange() {
- int currentLastVisibleItem = mCurrentFirstVisibleItem + mCurrentVisibleItemCount;
- int previousLastVisibleItem = mPreviousFirstVisibleItem + mPreviousVisibleItemCount;
- if (currentLastVisibleItem != previousLastVisibleItem) {
- if (mCellIsMobile && mMobileItemId != INVALID_ID) {
- updateNeighborViewsForId(mMobileItemId);
- handleCellSwitch();
- }
- }
- }
- };
-
- /**
- * Set the {@link com.nhaarman.listviewanimations.widget.DynamicListView.OnItemMovedListener} to be notified when an item is dropped.
- */
- public void setOnItemMovedListener(OnItemMovedListener onItemMovedListener) {
- this.mOnItemMovedListener = onItemMovedListener;
- }
-
- /**
- * Interface, usually implemented by a {@link com.nhaarman.listviewanimations.BaseAdapterDecorator},
- * that indicates that it can swap the visual position of two list items.
- *
- * @author Anton Spaans on 9/11/13.
- */
- public interface Swappable {
-
- /**
- * Swaps the item on the first adapter position with the item on the second adapter position.
- * Be sure to call {@link android.widget.BaseAdapter#notifyDataSetChanged()} if appropriate when implementing this method.
- *
- * @param positionOne First adapter position.
- * @param positionTwo Second adapter position.
- */
- public void swapItems(int positionOne, int positionTwo);
- }
-}
\ No newline at end of file
diff --git a/library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/matchers/Matchers.java b/library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/matchers/Matchers.java
deleted file mode 100644
index 1f0ab7b8..00000000
--- a/library/tests/java/com/nhaarman/listviewanimations/itemmanipulation/tests/matchers/Matchers.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.nhaarman.listviewanimations.itemmanipulation.tests.matchers;
-
-import android.util.Pair;
-
-import org.hamcrest.Description;
-import org.hamcrest.Factory;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class Matchers {
-
- @Factory
- public static Matcher> pairWithValues(T first, F second) {
- return new PairWithValues(first, second);
- }
-
- @Factory
- public static Matcher atomicIntegerWithValue(int value) {
- return new AtomicIntegerWithValue(value);
- }
-
- private static class PairWithValues extends TypeSafeMatcher> {
-
- private T first;
- private F second;
-
- public PairWithValues(T first, F second) {
- this.first = first;
- this.second = second;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Dunno!");
- }
-
- @Override
- protected boolean matchesSafely(Pair tfPair) {
- return tfPair.first.equals(first) && tfPair.second.equals(second);
- }
- }
-
- private static class AtomicIntegerWithValue extends TypeSafeMatcher {
-
- private int mValue;
-
- public AtomicIntegerWithValue(int value) {
- mValue = value;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText(String.valueOf(mValue));
- }
-
- @Override
- protected boolean matchesSafely(AtomicInteger atomicInteger) {
- return mValue == atomicInteger.intValue();
- }
- }
-}
diff --git a/pom.xml b/pom.xml
index f11ece37..61950b56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,179 +1,277 @@
-
- 4.0.0
-
-
- org.sonatype.oss
- oss-parent
- 7
-
-
- com.haarman.listviewanimations
- parent
- pom
-
- ListViewAnimations
- An Android library which allows developers to easily add animations to ListView items
- https://github.com/nhaarman/ListViewAnimations
- 2013
- 1.0.0-SNAPSHOT
-
-
- library
- example
-
-
-
- https://github.com/nhaarman/ListViewAnimations
- scm:git:git://github.com/nhaarman/ListViewAnimations.git
- scm:git:git@github.com:nhaarman/ListViewAnimations.git
- HEAD
-
-
-
-
- Niek Haarman
- haarman.niek@gmail.com
-
-
-
-
-
- Apache License Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
-
-
-
-
- GitHub Issues
- https://github.com/nhaarman/ListViewAnimations/issues
-
-
-
-
- UTF-8
- UTF-8
- 1.6
- 16
-
-
- 4.1.1.4
- r7
- 2.4.0
- 4.10
-
-
- 3.5.1
- 1.7
- 2.3.2
- 3.0
- 2.9
- 2.2.1
- 1.4
-
-
-
-
-
-
- com.google.android
- android
- ${android.version}
-
-
- com.google.android
- support-v4
- ${android-support.version}
-
-
-
-
- com.nineoldandroids
- library
- ${nineoldandroids.version}
-
-
-
-
- junit
- junit
- ${junit.version}
- test
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- ${maven-release-plugin.version}
-
- true
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
-
- ${java.version}
-
-
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- ${android-maven-plugin.version}
-
-
- ${android.sdk.platform}
-
- true
- false
- true
- ${project.basedir}/lint.xml
- false
- true
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- ${build-helper-maven-plugin.version}
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- ${maven-source-plugin.version}
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- ${maven-javadoc-plugin.version}
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- ${maven-release-plugin.version}
-
- @{project.version}
- true
-
-
-
-
-
+
+
+ 4.0.0
+
+
+ org.sonatype.oss
+ oss-parent
+ 7
+
+
+ com.nhaarman.listviewanimations
+ parent
+ pom
+
+ ListViewAnimations (Parent)
+ An Android library which allows developers to easily add animations to ListView items
+ https://github.com/nhaarman/ListViewAnimations
+ 2013
+ 3.0.0-SNAPSHOT
+
+
+ lib-core
+ lib-core-slh
+ lib-manipulation
+ example
+
+
+
+ https://github.com/nhaarman/ListViewAnimations
+ scm:git:git://github.com/nhaarman/ListViewAnimations.git
+ scm:git:git@github.com:nhaarman/ListViewAnimations.git
+ HEAD
+
+
+
+
+ Niek Haarman
+ haarman.niek@gmail.com
+
+
+
+
+
+ Apache License Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ GitHub Issues
+ https://github.com/nhaarman/ListViewAnimations/issues
+
+
+
+
+ UTF-8
+ UTF-8
+ 1.7
+ 19
+
+
+ 4.4.2_r3
+ 19.1.0
+ 2.4.0
+ 4.11
+ 1.9.5
+ 1.3
+
+
+ 3.9.0-rc.2
+ 1.7
+ 2.3.2
+ 3.0
+ 2.9
+ 2.2.1
+ 1.4
+ 1.3.2
+
+
+
+
+
+
+ com.google.android
+ android
+ ${android.version}
+
+
+ com.google.android
+ support-v4
+ ${android-support.version}
+
+
+ com.android.support
+ support-annotations
+ ${android-support.version}
+ jar
+
+
+
+
+ com.nineoldandroids
+ library
+ ${nineoldandroids.version}
+
+
+
+
+ se.emilsjolander
+ stickylistheaders
+ 2.4.0
+ aar
+
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.hamcrest
+ *
+
+
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.hamcrest
+ *
+
+
+
+
+ org.hamcrest
+ hamcrest-all
+ ${hamcrest.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+
+ true
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+ ${java.version}
+
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ ${android-maven-plugin.version}
+
+
+ ${android.sdk.platform}
+
+ true
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${build-helper-maven-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven-source-plugin.version}
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+ ${maven-jarsigner-plugin.version}
+
+
+
+
+
+
+
+ release
+
+
+ performRelease
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+ verify
+
+ package
+
+ true
+
+ ${sign.keystore}
+ ${sign.alias}
+ ${sign.storepass}
+ ${sign.keypass}
+ true
+ https://timestamp.geotrust.com/tsa
+
+ -sigalg
+ MD5withRSA
+ -digestalg
+ SHA1
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/publishGhPages.gradle b/publishGhPages.gradle
new file mode 100644
index 00000000..760950c7
--- /dev/null
+++ b/publishGhPages.gradle
@@ -0,0 +1,46 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'org.ajoberstar:gradle-git:0.9.+'
+ }
+}
+
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+import org.ajoberstar.grgit.*
+def repo = Grgit.open(".")
+
+def branch = repo.branch.current.name
+if(project.hasProperty('BRANCH')) {
+ branch = BRANCH
+}
+
+githubPages {
+
+ repoUri = 'https://github.com/nhaarman/ListViewAnimations.git'
+ commitMessage = 'Updated docs for branch ' + branch + ', module ' + project.name
+ pages {
+ from './build/docs/'
+ }
+ credentials {
+ username = System.getenv('GH_TOKEN_LVA')
+ password = '\n'
+ }
+}
\ No newline at end of file
diff --git a/maven_push.gradle b/pushMaven.gradle
similarity index 66%
rename from maven_push.gradle
rename to pushMaven.gradle
index f2e3915d..5fd94534 100644
--- a/maven_push.gradle
+++ b/pushMaven.gradle
@@ -1,13 +1,30 @@
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
apply plugin: 'maven'
apply plugin: 'signing'
def sonatypeRepositoryUrl
-if (isReleaseBuild()) {
- println 'RELEASE BUILD'
+if (isRelease == 'true') {
sonatypeRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+ println 'RELEASE BUILD ' + version
} else {
- println 'DEBUG BUILD'
sonatypeRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
+ version = VERSION_NAME + '-SNAPSHOT'
+ println 'DEBUG BUILD ' + version
}
afterEvaluate { project ->
@@ -54,28 +71,26 @@ afterEvaluate { project ->
}
signing {
- required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
+ required { isRelease == 'true' && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
- source = android.sourceSets.main.allJava
+ source = android.sourceSets.main.java
}
task androidJavadocsJar(type: Jar) {
classifier = 'javadoc'
- //basename = artifact_id
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
- //basename = artifact_id
- from android.sourceSets.main.allSource
+ from android.sourceSets.main.java.sourceFiles
}
task coreLibJar(type: Jar) {
- from fileTree(dir: 'build/classes/release').matching { include 'com/nhaarman/listviewanimations/**' }
+ from fileTree(dir: './build/intermediates/classes/release').matching { include 'com/nhaarman/listviewanimations/**' }
}
artifacts {
diff --git a/settings.gradle b/settings.gradle
index 507c9f4a..eb0423b0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,17 @@
-include ':library'
-include ':example'
+/*
+ * Copyright 2014 Niek Haarman
+ *
+ * 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.
+ */
+
+include ':example', 'lib-core', 'lib-manipulation', 'lib-core-slh'
\ No newline at end of file