├── .circleci └── config.yml ├── .gitignore ├── Contributing.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── willowtreeapps │ │ └── spurceexampleapp │ │ ├── App.java │ │ ├── ListViewActivity.java │ │ ├── RecyclerActivity.java │ │ ├── SplashActivity.java │ │ ├── SpruceActivity.java │ │ ├── fragments │ │ ├── ControlsFragment.java │ │ ├── ListViewFragment.java │ │ ├── RecyclerFragment.java │ │ └── ViewFragment.java │ │ ├── helpers │ │ └── InterpolatorSelector.java │ │ ├── model │ │ └── ExampleData.java │ │ ├── pager │ │ └── VerticalViewPager.java │ │ └── widgets │ │ ├── CardLayout.java │ │ └── RadioGroupGridLayout.java │ └── res │ ├── drawable-hdpi │ ├── ic_arrow_drop_down_white_24dp.png │ └── spruce_logo.png │ ├── drawable-mdpi │ ├── ic_arrow_drop_down_white_24dp.png │ └── spruce_logo.png │ ├── drawable-xhdpi │ ├── ic_arrow_drop_down_white_24dp.png │ └── spruce_logo.png │ ├── drawable-xxhdpi │ ├── ic_arrow_drop_down_white_24dp.png │ └── spruce_logo.png │ ├── drawable-xxxhdpi │ ├── ic_arrow_drop_down_white_24dp.png │ └── spruce_logo.png │ ├── drawable │ ├── code_sample_background.xml │ └── ic_keyboard_arrow_up_24dp.xml │ ├── layout │ ├── activity_splash.xml │ ├── continuous_radio_group.xml │ ├── controls_fragment.xml │ ├── cornered_radio_group.xml │ ├── directional_radio_group.xml │ ├── fragment_pager.xml │ ├── list_view_fragment.xml │ ├── positional_radio_group.xml │ ├── recycler_fragment.xml │ ├── spinner_item.xml │ ├── view_card.xml │ ├── view_fragment.xml │ └── view_placeholder.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── sort_functions_array.xml │ ├── string.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── code-of-conduct.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imgs ├── bottom-right-corner.gif ├── header_image.png ├── recycler-example.gif ├── top-left-corner.gif └── willowtree_logo.png ├── lib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── willowtreeapps │ │ └── spruce │ │ ├── AnimationActivity.java │ │ └── dynamics │ │ ├── SpruceFlingTests.java │ │ └── SpruceSpringTests.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── willowtreeapps │ │ │ └── spruce │ │ │ ├── Spruce.java │ │ │ ├── SpruceAnimator.java │ │ │ ├── animation │ │ │ └── DefaultAnimations.java │ │ │ ├── dynamics │ │ │ ├── AnimationHandler.kt │ │ │ ├── DynamicAnimation.kt │ │ │ ├── DynamicAnimatorSet.java │ │ │ ├── FloatPropertyCompat.java │ │ │ ├── FloatValueHolder.java │ │ │ ├── Force.java │ │ │ ├── SpringForce.java │ │ │ ├── SpruceDynamics.java │ │ │ ├── SpruceFlingAnimation.java │ │ │ └── SpruceSpringAnimation.java │ │ │ ├── exclusion │ │ │ └── ExclusionHelper.java │ │ │ ├── interpolators │ │ │ └── SpruceInterpolators.java │ │ │ └── sort │ │ │ ├── ContinuousSort.java │ │ │ ├── ContinuousWeightedSort.java │ │ │ ├── CorneredSort.java │ │ │ ├── DefaultSort.java │ │ │ ├── DistancedSort.java │ │ │ ├── InlineSort.java │ │ │ ├── LinearSort.java │ │ │ ├── RadialSort.java │ │ │ ├── RandomSort.java │ │ │ ├── SnakeSort.java │ │ │ ├── SortFunction.java │ │ │ ├── SpruceTimedView.java │ │ │ └── Utils.java │ └── res │ │ └── layout │ │ └── anim_layout.xml │ └── test │ └── java │ └── com │ └── willowtreeapps │ └── spruce │ ├── exclusion │ ├── ExclusionHelperTest.java │ └── ExclusionTestHelper.java │ └── sort │ ├── ContinuousSortTest.java │ ├── ContinuousWeightedSortTest.java │ ├── CorneredSortTest.java │ ├── DefaultSortTest.java │ ├── DistancedSortTest.java │ ├── InlineSortTest.java │ ├── RadialSortTest.java │ ├── RandomSortTest.java │ ├── ShadowPointF.java │ └── TestHelper.java └── settings.gradle /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/code 5 | docker: 6 | - image: circleci/android:api-28 7 | environment: 8 | JVM_OPTS: -Xmx3200m 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "lib/build.gradle" }} 13 | # - run: 14 | # name: Chmod permissions #if permission for Gradlew Dependencies fail, use this. 15 | # command: sudo chmod +x ./gradlew 16 | - run: 17 | name: Download Dependencies 18 | command: ./gradlew androidDependencies 19 | - save_cache: 20 | paths: 21 | - ~/.gradle 22 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "lib/build.gradle" }} 23 | - run: 24 | name: Run Tests 25 | command: ./gradlew lint test 26 | - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ 27 | path: lib/build/reports 28 | destination: reports 29 | - store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/ 30 | path: lib/build/test-results -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | *.jks 41 | libs/ 42 | src/main/java/ 43 | src/main/res/drawable/ 44 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | ### Creating the Pull Request 2 | To contribute, fork our project on GitHub, then submit a pull request to our `master` branch. 3 | 4 | ### Before Submitting 5 | 1. Make sure you unit test your changes. ~/lib/src/test/ houses all of the current unit tests. 6 | 2. If you added a `SortFunction` or anything that could have unit tests, please be sure to add them to the main project. 7 | 3. If you update anything that would add breaking changes to older versions of `Spruce` be sure to declare 8 | that in your Pull Request description. 9 | 4. Be sure to document your code. This includes adding in javadoc style comments for those methods that 10 | are publicly accessible and also adding those methods to their respective `README.md` document. 11 | 5. Most importantly, animations are fun! So have fun coding it!! 12 | 13 | --- 14 | 15 | By submitting a pull request, you represent that you have the right to license 16 | your contribution to WillowTree and the community, and agree by submitting the patch 17 | that your contributions are licensed under the [MIT License](LICENSE). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 WillowTree, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | apply plugin: 'com.android.application' 24 | 25 | android { 26 | compileSdkVersion rootProject.ext.compile_sdk_version 27 | 28 | defaultConfig { 29 | applicationId "com.willowtreeapps.spurceexampleapp" 30 | minSdkVersion rootProject.ext.min_sdk_version 31 | targetSdkVersion rootProject.ext.target_sdk_version 32 | versionCode rootProject.ext.version_code 33 | versionName rootProject.ext.version_name 34 | multiDexEnabled true 35 | 36 | } 37 | buildTypes { 38 | release { 39 | minifyEnabled false 40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 41 | } 42 | } 43 | } 44 | 45 | dependencies { 46 | implementation "androidx.appcompat:appcompat:$androidx_appcompact" 47 | implementation project(':lib') 48 | implementation "androidx.recyclerview:recyclerview:$recycler_view" 49 | implementation "androidx.multidex:multidex:$multidex" 50 | implementation "com.jakewharton.timber:timber:$timber" 51 | } 52 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/bretfears/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 26 | 27 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 50 | 51 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp; 24 | 25 | import android.app.Application; 26 | import timber.log.Timber; 27 | 28 | public class App extends Application { 29 | 30 | @Override 31 | public void onCreate() { 32 | super.onCreate(); 33 | 34 | if (BuildConfig.DEBUG) { 35 | Timber.plant(new Timber.DebugTree()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/ListViewActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp; 24 | 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | import android.view.Menu; 28 | import android.view.MenuItem; 29 | 30 | import com.willowtreeapps.spurceexampleapp.fragments.ListViewFragment; 31 | 32 | import androidx.annotation.Nullable; 33 | import androidx.appcompat.app.AppCompatActivity; 34 | import androidx.appcompat.widget.Toolbar; 35 | import androidx.fragment.app.Fragment; 36 | import androidx.fragment.app.FragmentManager; 37 | 38 | public class ListViewActivity extends AppCompatActivity { 39 | 40 | @Override 41 | protected void onCreate(@Nullable Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.list_view_fragment); 44 | 45 | FragmentManager fm = getSupportFragmentManager(); 46 | Fragment listViewFragment = fm.findFragmentById(R.id.list_view_fragment); 47 | if (listViewFragment == null) { 48 | listViewFragment = ListViewFragment.newInstance(); 49 | fm.beginTransaction() 50 | .replace(R.id.list_view_fragment, listViewFragment) 51 | .commit(); 52 | } 53 | 54 | Toolbar toolBar = (Toolbar) findViewById(R.id.list_view_tool_bar); 55 | setSupportActionBar(toolBar); 56 | 57 | if (getSupportActionBar() != null) { 58 | getSupportActionBar().setTitle(R.string.list_view_name); 59 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 60 | } 61 | } 62 | 63 | @Override 64 | public boolean onCreateOptionsMenu(Menu menu) { 65 | getMenuInflater().inflate(R.menu.menu_main, menu); 66 | return super.onCreateOptionsMenu(menu); 67 | } 68 | 69 | @Override 70 | public boolean onOptionsItemSelected(MenuItem item) { 71 | switch (item.getItemId()) { 72 | case R.id.sort_option: 73 | startActivity(new Intent(this, SpruceActivity.class) 74 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 75 | break; 76 | case R.id.list_view_option: 77 | break; 78 | case R.id.recycler_option: 79 | startActivity(new Intent(this, RecyclerActivity.class) 80 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 81 | break; 82 | default: 83 | finish(); 84 | break; 85 | } 86 | return super.onOptionsItemSelected(item); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/RecyclerActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp; 24 | 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | import android.view.Menu; 28 | import android.view.MenuItem; 29 | 30 | import com.willowtreeapps.spurceexampleapp.fragments.RecyclerFragment; 31 | 32 | import androidx.annotation.Nullable; 33 | import androidx.appcompat.app.AppCompatActivity; 34 | import androidx.appcompat.widget.Toolbar; 35 | import androidx.fragment.app.Fragment; 36 | import androidx.fragment.app.FragmentManager; 37 | 38 | 39 | public class RecyclerActivity extends AppCompatActivity { 40 | 41 | @Override 42 | protected void onCreate(@Nullable Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | 45 | setContentView(R.layout.recycler_fragment); 46 | 47 | FragmentManager fm = getSupportFragmentManager(); 48 | Fragment recyclerFragment = fm.findFragmentById(R.id.recycler_fragment); 49 | if (recyclerFragment == null) { 50 | recyclerFragment = RecyclerFragment.newInstance(); 51 | fm.beginTransaction() 52 | .replace(R.id.recycler_fragment, recyclerFragment) 53 | .commit(); 54 | } 55 | 56 | Toolbar toolBar = (Toolbar) findViewById(R.id.recycler_tool_bar); 57 | setSupportActionBar(toolBar); 58 | 59 | 60 | if (getSupportActionBar() != null) { 61 | getSupportActionBar().setTitle(R.string.recycler_name); 62 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 63 | } 64 | } 65 | 66 | @Override 67 | public boolean onCreateOptionsMenu(Menu menu) { 68 | getMenuInflater().inflate(R.menu.menu_main, menu); 69 | return super.onCreateOptionsMenu(menu); 70 | } 71 | 72 | @Override 73 | public boolean onOptionsItemSelected(MenuItem item) { 74 | switch (item.getItemId()) { 75 | case R.id.sort_option: 76 | startActivity(new Intent(this, SpruceActivity.class) 77 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 78 | break; 79 | case R.id.recycler_option: 80 | break; 81 | case R.id.list_view_option: 82 | startActivity(new Intent(this, ListViewActivity.class) 83 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 84 | break; 85 | default: 86 | finish(); 87 | break; 88 | } 89 | return super.onOptionsItemSelected(item); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/SplashActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp; 24 | 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | import android.os.Handler; 28 | import android.widget.ImageView; 29 | 30 | import androidx.annotation.Nullable; 31 | import androidx.appcompat.app.AppCompatActivity; 32 | import androidx.core.content.ContextCompat; 33 | 34 | public class SplashActivity extends AppCompatActivity { 35 | 36 | private static int SPLASH_TIMEOUT = 2000; 37 | private ImageView logoImageView; 38 | 39 | @Override 40 | protected void onCreate(@Nullable Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.activity_splash); 43 | 44 | logoImageView = (ImageView) findViewById(R.id.ivLogo); 45 | logoImageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.spruce_logo)); 46 | 47 | Handler handler = new Handler(); 48 | handler.postDelayed(new Runnable() { 49 | @Override 50 | public void run() { 51 | startActivity(new Intent(SplashActivity.this, SpruceActivity.class)); 52 | finish(); 53 | } 54 | }, SPLASH_TIMEOUT); 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/SpruceActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp; 24 | 25 | import android.content.Intent; 26 | import android.os.Bundle; 27 | import android.view.Menu; 28 | import android.view.MenuItem; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.widget.ArrayAdapter; 32 | import android.widget.Spinner; 33 | 34 | import com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment; 35 | import com.willowtreeapps.spurceexampleapp.fragments.ViewFragment; 36 | import com.willowtreeapps.spurceexampleapp.pager.VerticalViewPager; 37 | 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | 41 | import androidx.annotation.Nullable; 42 | import androidx.appcompat.app.AppCompatActivity; 43 | import androidx.appcompat.widget.Toolbar; 44 | import androidx.fragment.app.Fragment; 45 | import androidx.fragment.app.FragmentManager; 46 | import androidx.fragment.app.FragmentStatePagerAdapter; 47 | 48 | 49 | public class SpruceActivity extends AppCompatActivity 50 | implements ViewFragment.OnParentAndChildCreationListener { 51 | 52 | public ViewGroup parent; 53 | public List children = new ArrayList<>(); 54 | public Spinner sortDropDown; 55 | 56 | @Override 57 | protected void onCreate(@Nullable Bundle savedInstanceState) { 58 | super.onCreate(savedInstanceState); 59 | 60 | setContentView(R.layout.fragment_pager); 61 | 62 | FragmentManager fm = getSupportFragmentManager(); 63 | 64 | VerticalViewPager verticalPager = findViewById(R.id.vertical_pager); 65 | VerticalPagerAdapter adapter = new VerticalPagerAdapter(fm); 66 | verticalPager.setAdapter(adapter); 67 | 68 | Toolbar toolBar = findViewById(R.id.tool_bar); 69 | setSupportActionBar(toolBar); 70 | if (getSupportActionBar() != null) { 71 | getSupportActionBar().setDisplayShowTitleEnabled(false); 72 | } 73 | 74 | sortDropDown = findViewById(R.id.sort_selection); 75 | ArrayAdapter spinnerAdapter = ArrayAdapter.createFromResource(this, 76 | R.array.sort_functions, 77 | R.layout.spinner_item); 78 | spinnerAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); 79 | sortDropDown.setAdapter(spinnerAdapter); 80 | } 81 | 82 | @Override 83 | public boolean onCreateOptionsMenu(Menu menu) { 84 | getMenuInflater().inflate(R.menu.menu_main, menu); 85 | return super.onCreateOptionsMenu(menu); 86 | } 87 | 88 | @Override 89 | public boolean onOptionsItemSelected(MenuItem item) { 90 | switch (item.getItemId()) { 91 | case R.id.sort_option: 92 | break; 93 | case R.id.recycler_option: 94 | startActivity(new Intent(this, RecyclerActivity.class) 95 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 96 | break; 97 | case R.id.list_view_option: 98 | startActivity(new Intent(this, ListViewActivity.class) 99 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)); 100 | break; 101 | } 102 | return super.onOptionsItemSelected(item); 103 | } 104 | 105 | @Override 106 | public void onParentAndChildrenPrepared(ViewGroup parent, List children) { 107 | this.parent = parent; 108 | this.children = children; 109 | } 110 | 111 | private class VerticalPagerAdapter extends FragmentStatePagerAdapter { 112 | 113 | VerticalPagerAdapter(FragmentManager fm) { 114 | super(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); 115 | } 116 | 117 | @Override 118 | public Fragment getItem(int position) { 119 | switch (position) { 120 | case 1: 121 | return ControlsFragment.newInstance(); 122 | default: 123 | return ViewFragment.newInstance(); 124 | } 125 | } 126 | 127 | @Override 128 | public int getCount() { 129 | return 2; 130 | } 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/fragments/ViewFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp.fragments; 24 | 25 | import android.content.Context; 26 | import android.content.res.Resources; 27 | import android.os.Bundle; 28 | import android.util.TypedValue; 29 | import android.view.LayoutInflater; 30 | import android.view.View; 31 | import android.view.ViewGroup; 32 | import android.view.ViewTreeObserver; 33 | import android.widget.GridLayout; 34 | 35 | import androidx.annotation.Nullable; 36 | import androidx.fragment.app.Fragment; 37 | 38 | import com.willowtreeapps.spurceexampleapp.R; 39 | import com.willowtreeapps.spurceexampleapp.widgets.CardLayout; 40 | 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | 44 | 45 | public class ViewFragment extends Fragment { 46 | 47 | private OnParentAndChildCreationListener listener; 48 | private GridLayout parent; 49 | private final List children = new ArrayList<>(); 50 | 51 | public static ViewFragment newInstance() { 52 | return new ViewFragment(); 53 | } 54 | 55 | @Nullable 56 | @Override 57 | public View onCreateView(LayoutInflater inflater, ViewGroup container, @Nullable Bundle savedInstanceState) { 58 | ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.view_fragment, container, false); 59 | parent = rootView.findViewById(R.id.view_group_to_animate); 60 | 61 | final int CHILD_VIEW_COUNT = parent.getColumnCount() * parent.getRowCount(); 62 | 63 | for (int i = 0; i < CHILD_VIEW_COUNT; i++) { 64 | CardLayout childView = new CardLayout(getContext()); 65 | //setting ID for the exclusion logic. 66 | childView.setId(i); 67 | parent.addView(childView); 68 | children.add(childView); 69 | } 70 | 71 | parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 72 | @Override 73 | public void onGlobalLayout() { 74 | Resources res = getResources(); 75 | int tileMargins = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, res.getDisplayMetrics())); 76 | final int childWidth = parent.getWidth() / parent.getColumnCount() - (tileMargins * 2); 77 | final int childHeight = parent.getHeight() / parent.getRowCount() - (tileMargins * 2); 78 | 79 | for (int i = 0; i < parent.getChildCount(); i++) { 80 | View childView = parent.getChildAt(i); 81 | GridLayout.LayoutParams params = (GridLayout.LayoutParams) childView.getLayoutParams(); 82 | params.width = childWidth; 83 | params.height = childHeight; 84 | params.setMargins(tileMargins, tileMargins, tileMargins, tileMargins); 85 | childView.setLayoutParams(params); 86 | } 87 | parent.getViewTreeObserver().removeOnGlobalLayoutListener(this); 88 | } 89 | }); 90 | 91 | listener.onParentAndChildrenPrepared(parent, children); 92 | 93 | return rootView; 94 | } 95 | 96 | @Override 97 | public void onAttach(Context context) { 98 | super.onAttach(context); 99 | 100 | try { 101 | listener = (OnParentAndChildCreationListener) context; 102 | } catch (ClassCastException e) { 103 | throw new ClassCastException(context.toString() + " must implement OnParentAndChildCreationListener"); 104 | } 105 | } 106 | 107 | public interface OnParentAndChildCreationListener { 108 | void onParentAndChildrenPrepared(ViewGroup parent, List children); 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/helpers/InterpolatorSelector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp.helpers; 24 | 25 | import android.os.Build; 26 | import android.view.animation.Interpolator; 27 | import android.view.animation.LinearInterpolator; 28 | 29 | import androidx.annotation.RequiresApi; 30 | 31 | import com.willowtreeapps.spruce.interpolators.SpruceInterpolators; 32 | 33 | import java.util.HashMap; 34 | import java.util.Map; 35 | 36 | /** 37 | * This class is used to get the interpolator from {@link com.willowtreeapps.spruce.interpolators.SpruceInterpolators} 38 | *

39 | * This class is a helper for the Interpolation selection in {@link com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment} 40 | */ 41 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 42 | public class InterpolatorSelector { 43 | 44 | private final Map interpolatorMap = new HashMap<>(); 45 | 46 | public InterpolatorSelector() { 47 | initializeHashMap(); 48 | } 49 | 50 | /** 51 | * This hash map is used to the {@link android.app.AlertDialog} in 52 | * {@link com.willowtreeapps.spurceexampleapp.fragments.ControlsFragment} 53 | */ 54 | private void initializeHashMap() { 55 | interpolatorMap.put(0, SpruceInterpolators.EASE); 56 | interpolatorMap.put(1, SpruceInterpolators.EASE_IN); 57 | interpolatorMap.put(2, SpruceInterpolators.EASE_OUT); 58 | interpolatorMap.put(3, SpruceInterpolators.EASE_IN_OUT); 59 | interpolatorMap.put(4, SpruceInterpolators.EASE_IN_QUAD); 60 | interpolatorMap.put(5, SpruceInterpolators.EASE_IN_CUBIC); 61 | interpolatorMap.put(6, SpruceInterpolators.EASE_IN_QUART); 62 | interpolatorMap.put(7, SpruceInterpolators.EASE_IN_QUINT); 63 | interpolatorMap.put(8, SpruceInterpolators.EASE_IN_SINE); 64 | interpolatorMap.put(9, SpruceInterpolators.EASE_IN_EXPO); 65 | interpolatorMap.put(10, SpruceInterpolators.EASE_IN_CIRC); 66 | interpolatorMap.put(11, SpruceInterpolators.EASE_IN_BACK); 67 | interpolatorMap.put(12, SpruceInterpolators.EASE_OUT_QUAD); 68 | interpolatorMap.put(13, SpruceInterpolators.EASE_OUT_CUBIC); 69 | interpolatorMap.put(14, SpruceInterpolators.EASE_OUT_QUART); 70 | interpolatorMap.put(15, SpruceInterpolators.EASE_OUT_QUINT); 71 | interpolatorMap.put(16, SpruceInterpolators.EASE_OUT_SINE); 72 | interpolatorMap.put(17, SpruceInterpolators.EASE_OUT_EXPO); 73 | interpolatorMap.put(18, SpruceInterpolators.EASE_OUT_CIRC); 74 | interpolatorMap.put(19, SpruceInterpolators.EASE_OUT_BACK); 75 | interpolatorMap.put(20, SpruceInterpolators.EASE_IN_OUT_QUAD); 76 | interpolatorMap.put(21, SpruceInterpolators.EASE_IN_OUT_CUBIC); 77 | interpolatorMap.put(22, SpruceInterpolators.EASE_IN_OUT_QUART); 78 | interpolatorMap.put(23, SpruceInterpolators.EASE_IN_OUT_QUINT); 79 | interpolatorMap.put(24, SpruceInterpolators.EASE_IN_OUT_SINE); 80 | interpolatorMap.put(25, SpruceInterpolators.EASE_IN_OUT_EXPO); 81 | interpolatorMap.put(26, SpruceInterpolators.EASE_IN_OUT_CIRC); 82 | interpolatorMap.put(27, SpruceInterpolators.EASE_IN_OUT_BACK); 83 | } 84 | 85 | /** 86 | * This method returns the interpolator for a specific position from the {@link HashMap} 87 | * 88 | * @param position position from the {@link android.app.AlertDialog} 89 | * @return {@link Interpolator} that is selected view {@link android.app.AlertDialog} 90 | */ 91 | public Interpolator getInterpolatorMap(int position) { 92 | if (interpolatorMap.containsKey(position)) { 93 | return interpolatorMap.get(position); 94 | } else { 95 | return new LinearInterpolator(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/model/ExampleData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | 24 | package com.willowtreeapps.spurceexampleapp.model; 25 | 26 | public class ExampleData { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/pager/VerticalViewPager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp.pager; 24 | 25 | import android.content.Context; 26 | import android.util.AttributeSet; 27 | import android.view.MotionEvent; 28 | import android.view.View; 29 | 30 | import androidx.viewpager.widget.ViewPager; 31 | 32 | public class VerticalViewPager extends ViewPager { 33 | 34 | public VerticalViewPager(Context context) { 35 | super(context); 36 | init(); 37 | } 38 | 39 | public VerticalViewPager(Context context, AttributeSet attrs) { 40 | super(context, attrs); 41 | init(); 42 | } 43 | 44 | private void init() { 45 | setPageTransformer(true, new VerticalPageTransformer()); 46 | setOverScrollMode(OVER_SCROLL_NEVER); 47 | } 48 | 49 | @Override 50 | public boolean onInterceptTouchEvent(MotionEvent ev){ 51 | boolean intercepted = super.onInterceptTouchEvent(swapXY(ev)); 52 | swapXY(ev); 53 | return intercepted; 54 | } 55 | 56 | @Override 57 | public boolean onTouchEvent(MotionEvent ev) { 58 | return super.onTouchEvent(swapXY(ev)); 59 | } 60 | 61 | private MotionEvent swapXY(MotionEvent ev) { 62 | float width = getWidth(); 63 | float height = getHeight(); 64 | 65 | float newX = (ev.getY() / height) * width; 66 | float newY = (ev.getX() / width) * height; 67 | 68 | ev.setLocation(newX, newY); 69 | 70 | return ev; 71 | } 72 | 73 | private class VerticalPageTransformer implements ViewPager.PageTransformer { 74 | 75 | @Override 76 | public void transformPage(View view, float position) { 77 | 78 | if (position < -1) { 79 | view.setAlpha(0); 80 | 81 | } else if (position <= 1) { 82 | view.setAlpha(1); 83 | 84 | view.setTranslationX(view.getWidth() * -position); 85 | 86 | float yPosition = position * view.getHeight(); 87 | view.setTranslationY(yPosition); 88 | 89 | } else { 90 | view.setAlpha(0); 91 | } 92 | } 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /app/src/main/java/com/willowtreeapps/spurceexampleapp/widgets/CardLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spurceexampleapp.widgets; 24 | 25 | import android.content.Context; 26 | import android.util.AttributeSet; 27 | import android.view.LayoutInflater; 28 | import android.widget.LinearLayout; 29 | 30 | import com.willowtreeapps.spurceexampleapp.R; 31 | 32 | import androidx.annotation.Nullable; 33 | 34 | public class CardLayout extends LinearLayout { 35 | 36 | public CardLayout(Context context) { 37 | super(context); 38 | init(); 39 | } 40 | 41 | public CardLayout(Context context, @Nullable AttributeSet attrs) { 42 | super(context, attrs); 43 | init(); 44 | } 45 | 46 | public CardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 47 | super(context, attrs, defStyleAttr); 48 | init(); 49 | } 50 | 51 | private void init() { 52 | LayoutInflater inflater = LayoutInflater.from(getContext()); 53 | inflater.inflate(R.layout.view_card, this, true); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_drop_down_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-hdpi/ic_arrow_drop_down_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/spruce_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-hdpi/spruce_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_drop_down_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-mdpi/ic_arrow_drop_down_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/spruce_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-mdpi/spruce_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/spruce_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xhdpi/spruce_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_drop_down_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_down_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/spruce_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xxhdpi/spruce_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_down_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_down_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/spruce_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/drawable-xxxhdpi/spruce_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/code_sample_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_keyboard_arrow_up_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 30 | 31 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/continuous_radio_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 27 | 28 | 32 | 33 | 39 | 40 | 45 | 46 | 50 | 51 | 56 | 57 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 81 | 86 | 87 | 91 | 92 | 97 | 98 | 104 | 105 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/src/main/res/layout/cornered_radio_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 27 | 28 | 33 | 34 | 40 | 41 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/directional_radio_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 27 | 28 | 33 | 34 | 40 | 41 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_pager.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 29 | 30 | 36 | 37 | 43 | 44 | 45 | 46 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_view_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 29 | 30 | 36 | 37 | 38 | 39 | 44 | 45 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/positional_radio_group.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 27 | 28 | 34 | 35 | 41 | 42 | 47 | 48 | 53 | 54 | 59 | 60 | 65 | 66 | 71 | 72 | 77 | 78 | 83 | 84 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recycler_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 29 | 30 | 36 | 37 | 38 | 39 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/spinner_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_card.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 30 | 31 | 40 | 41 | 48 | 49 | 50 | 51 | 58 | 59 | 63 | 64 | 71 | 72 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_placeholder.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 29 | 30 | 40 | 41 | 52 | 53 | 65 | 66 | 78 | 79 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 |

26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 27 | 64dp 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | #3BB894 26 | #2e7d32 27 | #8BC34A 28 | #4bc495 29 | #FFFFFF 30 | #000000 31 | #DDDDDD 32 | #939393 33 | #F8F8F9 34 | #D9E1E2 35 | #DBE9F9 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 16dp 26 | 16dp 27 | 96dp 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/sort_functions_array.xml: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | 25 | 26 | @string/default_sort 27 | @string/cornered_sort 28 | @string/continuous_sort 29 | @string/continuous_weighted_sort 30 | @string/inline_sort 31 | @string/linear_sort 32 | @string/radial_sort 33 | @string/random_sort 34 | @string/snake_sort 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/values/string.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | ease 26 | easeIn 27 | easeOut 28 | easeInOut 29 | easeInQuad 30 | easeInCubic 31 | easeInQuart 32 | easeInQuint 33 | easeInSine 34 | easeInExpo 35 | easeInCirc 36 | easeInBack 37 | easeOutQuad 38 | easeOutCubic 39 | easeOutQuart 40 | easeOutQuint 41 | easeOutSine 42 | easeOutExpo 43 | easeOutCirc 44 | easeOutBack 45 | easeInOutQuad 46 | easeInOutCubic 47 | easeInOutQuart 48 | easeInOutQuint 49 | easeInOutSine 50 | easeInOutExpo 51 | easeInOutCirc 52 | easeInOutBack 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | Spruce 25 | Recycler Example 26 | List View Example 27 | 28 | 29 | Sort Example 30 | Recycler View Example 31 | List View Example 32 | 33 | Default Sort 34 | Cornered Sort 35 | Continuous Sort 36 | Continuous Weighted Sort 37 | Inline Sort 38 | Linear Sort 39 | Radial Sort 40 | Random Sort 41 | Snake Sort 42 | 43 | Bottom to Top 44 | Top to Bottom 45 | Left to Right 46 | Right to Left 47 | 48 | Top Left 49 | Top Middle 50 | Top Right 51 | Right 52 | Middle 53 | Left 54 | Bottom Left 55 | Bottom Middle 56 | Bottom Right 57 | 58 | Light 59 | Medium 60 | Heavy 61 | 62 | Reversed 63 | 64 | Vertical 65 | Horizontal 66 | 0s 67 | 0.1s 68 | 2s 69 | Duration 70 | Delay 71 | Sort function: 72 | Sort Function selection 73 | Select 74 | Cancel 75 | 76 | 77 | Code Sample 78 | new DefaultSort(%1$d); 79 | new CorneredSort(%1$d, %2$s, CorneredSort.Corner.%3$s); 80 | new ContinuousSort(%1$d, %2$s, RadialSort.Position.%3$s); 81 | new ContinuousWeightedSort(%1$d, %2$s, RadialSort.Position.%3$s, %4$s, %5$s); 82 | new InlineSort(%1$d, %2$s, CorneredSort.Corner.%3$s); 83 | new LinearSort(%1$d, %2$s, LinearSort.Direction.%3$s); 84 | new RadialSort(%1$d, %2$s, RadialSort.Position.%3$s); 85 | new RandomSort(%1$d); 86 | new SnakeSort(%1$d, %2$s, CorneredSort.Corner.%3$s); 87 | Sort Controls 88 | exclude views 89 | Interpolator 90 | linear 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 34 | 35 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.72' 5 | 6 | repositories { 7 | jcenter() 8 | google() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.5.4' 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 13 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5' 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | jcenter() 24 | google() 25 | } 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | 32 | // Define versions in a single place 33 | ext { 34 | // Sdk and Tools 35 | min_sdk_version = 16 36 | target_sdk_version = 30 37 | compile_sdk_version = 30 38 | 39 | //Library data 40 | version_name = '1.1.0' 41 | version_code = 1 42 | group_name = "com.willowtreeapps.spruce" 43 | lib_name = 'spruce-android' 44 | lib_desc = 'lightweight animation library' 45 | 46 | //dev details 47 | dev_id = 'willowtreeapps' 48 | dev_name = 'WillowTree Inc' 49 | dev_email = 'developer@willowtreeapps.com' 50 | 51 | //license 52 | license_name = 'MIT License' 53 | license_url = 'https://opensource.org/licenses/MIT' 54 | 55 | //General 56 | androidx_appcompact = '1.2.0' 57 | kotlin_collections_ktx = '1.1.0' 58 | recycler_view = '1.1.0' 59 | multidex = '2.0.1' 60 | timber = '4.7.1' 61 | kotlin_project_version = '1.3.2' 62 | 63 | //Local Test 64 | local_junit_version = '4.12' 65 | local_mokito_version = '2.19.0' 66 | local_robo_electric_version = '4.1' 67 | 68 | //UI Test 69 | ui_test_runner_version = '1.3.0' 70 | ui_junit_version = '1.1.2' 71 | ui_espresso_core_version = '3.1.0' 72 | ui_espresso_intent_version = '3.1.0' 73 | 74 | } -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at open.source.conduct@willowtreeapps.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Feb 04 15:27:17 EST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /imgs/bottom-right-corner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/imgs/bottom-right-corner.gif -------------------------------------------------------------------------------- /imgs/header_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/imgs/header_image.png -------------------------------------------------------------------------------- /imgs/recycler-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/imgs/recycler-example.gif -------------------------------------------------------------------------------- /imgs/top-left-corner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/imgs/top-left-corner.gif -------------------------------------------------------------------------------- /imgs/willowtree_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willowtreeapps/spruce-android/06924982d71c5fa73582678e4331a3f88e0b92dc/imgs/willowtree_logo.png -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/bretfears/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /lib/src/androidTest/java/com/willowtreeapps/spruce/AnimationActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce; 24 | 25 | import android.app.Activity; 26 | import android.os.Bundle; 27 | 28 | import com.willowtreeapps.spruce.R; 29 | 30 | public class AnimationActivity extends Activity { 31 | @Override 32 | public void onCreate(Bundle bundle) { 33 | super.onCreate(bundle); 34 | setContentView(R.layout.anim_layout); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/SpruceAnimator.java: -------------------------------------------------------------------------------- 1 | package com.willowtreeapps.spruce; 2 | 3 | import android.animation.AnimatorSet; 4 | 5 | import com.willowtreeapps.spruce.dynamics.DynamicAnimatorSet; 6 | 7 | /** 8 | * This is a wrapper class for holding animation set and {@link DynamicAnimatorSet} 9 | */ 10 | public class SpruceAnimator { 11 | private AnimatorSet animatorSet = new AnimatorSet(); 12 | private DynamicAnimatorSet dynamicAnimatorSet = new DynamicAnimatorSet(); 13 | 14 | public AnimatorSet getAnimatorSet() { 15 | return animatorSet; 16 | } 17 | 18 | public void setAnimatorSet(AnimatorSet animatorSet) { 19 | this.animatorSet = animatorSet; 20 | } 21 | 22 | public DynamicAnimatorSet getDynamicAnimatorSet() { 23 | return dynamicAnimatorSet; 24 | } 25 | 26 | public void setDynamicAnimatorSet(DynamicAnimatorSet dynamicAnimatorSet) { 27 | this.dynamicAnimatorSet = dynamicAnimatorSet; 28 | } 29 | 30 | /** 31 | * cancels all the animation that are presently performed. 32 | */ 33 | public void cancel() { 34 | animatorSet.cancel(); 35 | dynamicAnimatorSet.cancel(); 36 | } 37 | 38 | /** 39 | * starts all the animations that are queued. 40 | */ 41 | public void start() { 42 | animatorSet.start(); 43 | dynamicAnimatorSet.start(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/animation/DefaultAnimations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.animation; 24 | 25 | import android.animation.Animator; 26 | import android.animation.ObjectAnimator; 27 | import android.animation.PropertyValuesHolder; 28 | import android.view.View; 29 | 30 | import com.willowtreeapps.spruce.dynamics.SpruceDynamics; 31 | import com.willowtreeapps.spruce.dynamics.SpruceSpringAnimation; 32 | import com.willowtreeapps.spruce.dynamics.SpringForce; 33 | 34 | /** 35 | * Convenience methods for retrieving default view animators 36 | */ 37 | public class DefaultAnimations { 38 | 39 | private static final float GROW_SCALE = 1.5F; 40 | private static final float SHRINK_SCALE = 0.1F; 41 | private static final float ORIGINAL_SCALE = 1.0F; 42 | private static final float FADE_AWAY_TO = 0.0F; 43 | private static final float FADE_IN_TO = 1.0F; 44 | private static final float FADE_FROM = 0.0F; 45 | private static final float START_ROTATION = 0F; 46 | private static final float END_ROTATION = 360F; 47 | 48 | public static Animator growAnimator(View view, long duration) { 49 | return ObjectAnimator.ofPropertyValuesHolder(view, 50 | PropertyValuesHolder.ofFloat(View.SCALE_X, GROW_SCALE, ORIGINAL_SCALE), 51 | PropertyValuesHolder.ofFloat(View.SCALE_Y, GROW_SCALE, ORIGINAL_SCALE)) 52 | .setDuration(duration); 53 | } 54 | 55 | public static Animator shrinkAnimator(View view, long duration) { 56 | return ObjectAnimator.ofPropertyValuesHolder(view, 57 | PropertyValuesHolder.ofFloat(View.SCALE_X, SHRINK_SCALE, ORIGINAL_SCALE), 58 | PropertyValuesHolder.ofFloat(View.SCALE_Y, SHRINK_SCALE, ORIGINAL_SCALE)) 59 | .setDuration(duration); 60 | } 61 | 62 | public static Animator fadeAwayAnimator(View view, long duration) { 63 | return ObjectAnimator.ofFloat(view, View.ALPHA, FADE_AWAY_TO) 64 | .setDuration(duration); 65 | } 66 | 67 | public static Animator fadeInAnimator(View view, long duration) { 68 | return ObjectAnimator.ofFloat(view, View.ALPHA, FADE_FROM, FADE_IN_TO) 69 | .setDuration(duration); 70 | } 71 | 72 | public static Animator spinAnimator(View view, long duration) { 73 | return ObjectAnimator.ofFloat(view, View.ROTATION, START_ROTATION, END_ROTATION) 74 | .setDuration(duration); 75 | } 76 | 77 | public static SpruceSpringAnimation dynamicTranslationUpwards(View view) { 78 | SpruceSpringAnimation tranUp = new SpruceSpringAnimation(view, SpruceDynamics.TRANSLATION_Y) 79 | .setStartValue(200f); 80 | tranUp.setSpring(new SpringForce()); 81 | tranUp.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY); 82 | tranUp.getSpring().setStiffness(SpringForce.STIFFNESS_LOW); 83 | tranUp.getSpring().setFinalPosition(FADE_FROM); 84 | return tranUp; 85 | } 86 | 87 | public static SpruceSpringAnimation dynamicFadeIn(View view) { 88 | SpruceSpringAnimation tranUp = new SpruceSpringAnimation(view, SpruceDynamics.ALPHA) 89 | .setStartValue(FADE_FROM); 90 | tranUp.setSpring(new SpringForce()); 91 | tranUp.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY); 92 | tranUp.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM); 93 | tranUp.getSpring().setFinalPosition(FADE_IN_TO); 94 | return tranUp; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/dynamics/DynamicAnimation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.dynamics 24 | 25 | /** 26 | * Creates [SpruceFlingAnimation] for a property that can be accessed via the provided setter and getter. 27 | * For example, the following sample code creates a [SpruceFlingAnimation] for the alpha property of a 28 | * [View] object named `view`: 29 | * `flingAnimationOf(view::setAlpha, view::getAlpha)` 30 | * 31 | * @param setter The function that mutates the property being animated 32 | * @param getter The function that returns the value of the property 33 | * @return [SpruceFlingAnimation] 34 | */ 35 | fun flingAnimationOf(setter: (Float) -> Unit, getter: () -> Float): SpruceFlingAnimation { 36 | return SpruceFlingAnimation(createFloatValueHolder(setter, getter)) 37 | } 38 | 39 | /** 40 | * Creates [SpruceSpringAnimation] for a property that can be accessed via the provided setter and getter. 41 | * If finalPosition is not [Float.NaN] then create [SpruceSpringAnimation] with 42 | * [SpringForce.mFinalPosition]. 43 | * 44 | * @param setter The function that mutates the property being animated 45 | * @param getter The function that returns the value of the property 46 | * @param finalPosition [SpringForce.mFinalPosition] Final position of spring. 47 | * @return [SpruceSpringAnimation] 48 | */ 49 | fun springAnimationOf( 50 | setter: (Float) -> Unit, 51 | getter: () -> Float, 52 | finalPosition: Float = Float.NaN 53 | ): SpruceSpringAnimation { 54 | val valueHolder = createFloatValueHolder(setter, getter) 55 | return if (finalPosition.isNaN()) { 56 | SpruceSpringAnimation(valueHolder) 57 | } else { 58 | SpruceSpringAnimation(valueHolder, finalPosition) 59 | } 60 | } 61 | 62 | /** 63 | * Updates or applies spring force properties like [SpringForce.mDampingRatio], 64 | * [SpringForce.mFinalPosition] and stiffness on SpringAnimation. 65 | * 66 | * If [SpruceSpringAnimation.mSpring] is null in case [SpruceSpringAnimation] is created without final position 67 | * it will be created and attached to [SpruceSpringAnimation] 68 | * 69 | * @param func lambda with receiver on [SpringForce] 70 | * @return [SpruceSpringAnimation] 71 | */ 72 | inline fun SpruceSpringAnimation.withSpringForceProperties( 73 | func: SpringForce.() -> Unit 74 | ): SpruceSpringAnimation { 75 | if (spring == null) { 76 | spring = SpringForce() 77 | } 78 | spring.func() 79 | return this 80 | } 81 | 82 | private fun createFloatValueHolder(setter: (Float) -> Unit, getter: () -> Float): FloatValueHolder { 83 | return object : FloatValueHolder() { 84 | override fun getValue(): Float { 85 | return getter.invoke() 86 | } 87 | 88 | override fun setValue(value: Float) { 89 | setter.invoke(value) 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/dynamics/DynamicAnimatorSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.dynamics; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * This class is eqivalent to {@link android.view.animation.AnimationSet} for dynamic animations. 30 | */ 31 | public class DynamicAnimatorSet { 32 | 33 | private List> anim = new ArrayList<>(); 34 | 35 | /** 36 | * Sets up this AnimatorSet to play all of the supplied animations at the same time. 37 | * 38 | * @param items The animations that will be started simultaneously. 39 | */ 40 | public void playTogether(List> items) { 41 | anim = items; 42 | } 43 | 44 | /** 45 | * This method is used to cancel the animation. 46 | */ 47 | public void cancel() { 48 | for (SpruceDynamics anim : anim) { 49 | anim.cancel(); 50 | } 51 | } 52 | 53 | 54 | /** 55 | * This method is used to start all queued dynamic animations. 56 | */ 57 | public void start() { 58 | for (SpruceDynamics anim : anim) { 59 | anim.start(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/dynamics/FloatPropertyCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.dynamics; 24 | 25 | import android.util.FloatProperty; 26 | 27 | import androidx.annotation.RequiresApi; 28 | 29 | /** 30 | *

FloatPropertyCompat is an abstraction that can be used to represent a mutable float value that 31 | * is held in a host object. To access this float value, {@link #setValue(Object, float)} and getter 32 | * {@link #getValue(Object)} need to be implemented. Both the setter and the getter take the 33 | * primitive float type and avoids autoboxing and other overhead associated with the 34 | * Float class. 35 | * 36 | *

For API 24 and later, {@link FloatProperty} instances can be converted to 37 | * {@link FloatPropertyCompat} through 38 | * {@link FloatPropertyCompat#createFloatPropertyCompat(FloatProperty)}. 39 | * 40 | * @param the class on which the Property is declared 41 | */ 42 | public abstract class FloatPropertyCompat { 43 | final String mPropertyName; 44 | 45 | /** 46 | * A constructor that takes an identifying name. 47 | */ 48 | public FloatPropertyCompat(String name) { 49 | mPropertyName = name; 50 | } 51 | 52 | /** 53 | * Create a {@link FloatPropertyCompat} wrapper for a {@link FloatProperty} object. The new 54 | * {@link FloatPropertyCompat} instance will access and modify the property value of 55 | * {@link FloatProperty} through the {@link FloatProperty} instance's setter and getter. 56 | * 57 | * @param property FloatProperty instance to be wrapped 58 | * @param the class on which the Property is declared 59 | * @return a new {@link FloatPropertyCompat} wrapper for the given {@link FloatProperty} object 60 | */ 61 | @RequiresApi(24) 62 | public static FloatPropertyCompat createFloatPropertyCompat( 63 | final FloatProperty property) { 64 | return new FloatPropertyCompat(property.getName()) { 65 | @Override 66 | public float getValue(T object) { 67 | return property.get(object); 68 | } 69 | 70 | @Override 71 | public void setValue(T object, float value) { 72 | property.setValue(object, value); 73 | } 74 | }; 75 | } 76 | 77 | /** 78 | * Returns the current value that this property represents on the given object. 79 | * 80 | * @param object object which this property represents 81 | * @return the current property value of the given object 82 | */ 83 | public abstract float getValue(T object); 84 | 85 | /** 86 | * Sets the value on object which this property represents. 87 | * 88 | * @param object object which this property represents 89 | * @param value new value of the property 90 | */ 91 | public abstract void setValue(T object, float value); 92 | } 93 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/dynamics/FloatValueHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.dynamics; 24 | 25 | /** 26 | *

FloatValueHolder holds a float value. FloatValueHolder provides a setter and a getter ( 27 | * i.e. {@link #setValue(float)} and {@link #getValue()}) to access this float value. Animations can 28 | * be performed on a FloatValueHolder instance. During each frame of the animation, the 29 | * FloatValueHolder will have its value updated via {@link #setValue(float)}. The caller can 30 | * obtain the up-to-date animation value via {@link FloatValueHolder#getValue()}. 31 | * 32 | *

Here is an example for creating a {@link SpruceFlingAnimation} with a FloatValueHolder: 33 | *

34 |  * // Create a fling animation with an initial velocity of 5000 (pixel/s) and an initial position
35 |  * // of 20f.
36 |  * FloatValueHolder floatValueHolder = new FloatValueHolder(20f);
37 |  * FlingAnimation anim = new FlingAnimation(floatValueHolder).setStartVelocity(5000);
38 |  * anim.start();
39 |  * 
40 | * 41 | * @see SpruceSpringAnimation#SpruceSpringAnimation(FloatValueHolder) 42 | * @see SpruceFlingAnimation#SpruceFlingAnimation(FloatValueHolder) 43 | */ 44 | 45 | public class FloatValueHolder { 46 | private float mValue = 0.0f; 47 | 48 | /** 49 | * Constructs a holder for a float value that is initialized to 0. 50 | */ 51 | public FloatValueHolder() { 52 | } 53 | 54 | /** 55 | * Constructs a holder for a float value that is initialized to the input value. 56 | * 57 | * @param value the value to initialize the value held in the FloatValueHolder 58 | */ 59 | public FloatValueHolder(float value) { 60 | setValue(value); 61 | } 62 | 63 | /** 64 | * Sets the value held in the FloatValueHolder instance. 65 | * 66 | * @param value float value held in the FloatValueHolder instance 67 | */ 68 | public void setValue(float value) { 69 | mValue = value; 70 | } 71 | 72 | /** 73 | * Returns the float value held in the FloatValueHolder instance. 74 | * 75 | * @return float value held in the FloatValueHolder instance 76 | */ 77 | public float getValue() { 78 | return mValue; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/dynamics/Force.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.dynamics; 24 | 25 | /** 26 | * Hide this for now, in case we want to change the API. 27 | */ 28 | interface Force { 29 | // Acceleration based on position. 30 | float getAcceleration(float position, float velocity); 31 | 32 | boolean isAtEquilibrium(float value, float velocity); 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/exclusion/ExclusionHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.exclusion; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import java.util.ArrayList; 29 | import java.util.HashMap; 30 | import java.util.List; 31 | 32 | /** 33 | * This class takes care of the exclusion functionality 34 | */ 35 | public class ExclusionHelper { 36 | 37 | private final HashMap exclusionMap = new HashMap<>(); 38 | private int mode; 39 | public static final int R_L_MODE = 0; 40 | public static final int NORMAL_MODE = 1; 41 | 42 | /** 43 | * This method is used to initialize the exclusion module. 44 | * 45 | * @param exclusionList list of items to be excluded. 46 | * @param mode mode in which the exclusion works. 47 | */ 48 | public void initialize(List exclusionList, int mode) { 49 | /* 50 | * making hash-map from the given list. 51 | * This is to cut down the complexity in the future. 52 | */ 53 | this.mode = mode; 54 | for (Integer id : exclusionList) { 55 | exclusionMap.put(id, true); 56 | } 57 | } 58 | 59 | /** 60 | * isToBeIncluded function works in 2 modes, 61 | * 1. ID mode : exclude if the ID is present. 62 | * 2. Index mode: exclude a specific index. 63 | * 64 | * @param view the view to be checked for exclusion. 65 | * @param index index of the view/ 66 | * @return true of the view is to be included else excluded. 67 | */ 68 | public boolean isToBeIncluded(View view, int index) { 69 | if (mode == NORMAL_MODE) { 70 | return view != null && !exclusionMap.containsKey(view.getId()); 71 | } else if (mode == R_L_MODE) { 72 | return view != null && !exclusionMap.containsKey(index); 73 | } 74 | return false; 75 | } 76 | 77 | /** 78 | * filterViews function will filter out the {@link HashMap} exclusionMap from the parent view group. 79 | * 80 | * @param viewGroup parent view group 81 | * @return returns filtered list of type view 82 | */ 83 | public List filterViews(ViewGroup viewGroup) { 84 | List children = new ArrayList<>(); 85 | 86 | /* 87 | * The reason why we use hash map for the comparison is because 88 | * HashMap has complexity of O(1) for insertion and lookup. 89 | * this will keep the overall complexity as O(n). 90 | */ 91 | for (int index = 0; index < viewGroup.getChildCount(); index++) { 92 | View view = viewGroup.getChildAt(index); 93 | if (isToBeIncluded(view, index)) 94 | children.add(view); 95 | } 96 | return children; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/ContinuousSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class ContinuousSort extends RadialSort { 35 | 36 | private final long duration; 37 | private final boolean reversed; 38 | 39 | /** 40 | * Establishes the delay between object animations and their starting position based on distance, 41 | * delay, and a value from the Position enum 42 | * 43 | * @param interObjectDelay delay between object animations 44 | * @param reversed flag to indicate if the animation should be reversed 45 | * @param position enum value of the position the animation should start from 46 | */ 47 | public ContinuousSort(long interObjectDelay, boolean reversed, Position position) { 48 | super(interObjectDelay, reversed, position); 49 | this.duration = interObjectDelay; 50 | this.reversed = reversed; 51 | } 52 | 53 | @Override 54 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 55 | final PointF comparisonPoint = getDistancePoint(parent, children); 56 | 57 | double maxDistance = 0; 58 | for (View v : children) { 59 | double distance = getDistanceBetweenPoints(Utils.viewToPoint(v), comparisonPoint); 60 | if (distance > maxDistance) { 61 | maxDistance = distance; 62 | } 63 | } 64 | 65 | List timedViews = new ArrayList<>(); 66 | for (View view : children) { 67 | double normalizedDistance; 68 | double viewDistance = getDistanceBetweenPoints(Utils.viewToPoint(view), comparisonPoint); 69 | if (reversed) { 70 | normalizedDistance = (maxDistance - viewDistance) / maxDistance; 71 | } else { 72 | normalizedDistance = viewDistance / maxDistance; 73 | } 74 | 75 | long offset = Math.round(duration * normalizedDistance); 76 | timedViews.add(new SpruceTimedView(view, offset)); 77 | } 78 | 79 | return timedViews; 80 | } 81 | 82 | @Override 83 | public void sortChildren(ViewGroup parent, List children) { 84 | final PointF comparisonPoint = getDistancePoint(parent, children); 85 | Collections.sort(children, new Comparator() { 86 | @Override 87 | public int compare(View left, View right) { 88 | double leftDistance = getDistanceBetweenPoints(Utils.viewToPoint(left), comparisonPoint); 89 | double rightDistance = getDistanceBetweenPoints(Utils.viewToPoint(right), comparisonPoint); 90 | return Double.compare(leftDistance, rightDistance); 91 | } 92 | }); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/CorneredSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class CorneredSort extends DistancedSort { 35 | 36 | public enum Corner { 37 | TOP_LEFT, 38 | TOP_RIGHT, 39 | BOTTOM_LEFT, 40 | BOTTOM_RIGHT 41 | } 42 | 43 | private final long interObjectDelay; 44 | private final Corner corner; 45 | private final boolean reversed; 46 | 47 | /** 48 | * Animates views in a corner like fashion. The views near the starting corner will animate first. 49 | * 50 | * @param interObjectDelay long delay between objects 51 | * @param reversed boolean 52 | * @param corner Corner enum value {@link Corner corner} 53 | */ 54 | public CorneredSort(long interObjectDelay, boolean reversed, Corner corner) { 55 | super(interObjectDelay, reversed); 56 | if (corner == null) { 57 | throw new NullPointerException("Corner can't be null and must be a valid type"); 58 | } 59 | this.interObjectDelay = interObjectDelay; 60 | this.corner = corner; 61 | this.reversed = reversed; 62 | } 63 | 64 | @Override 65 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 66 | final PointF comparisonPoint = getDistancePoint(parent, children); 67 | List timedViews = new ArrayList<>(); 68 | long currentTimeOffset = 0; 69 | 70 | double lastDistance = 0; 71 | for (View view : children) { 72 | double viewDistance = getDistanceBetweenPoints(Utils.viewToPoint(view), comparisonPoint); 73 | if (Math.floor(lastDistance) != Math.floor(viewDistance)) { 74 | lastDistance = viewDistance; 75 | currentTimeOffset += interObjectDelay; 76 | } 77 | timedViews.add(new SpruceTimedView(view, currentTimeOffset)); 78 | } 79 | 80 | return timedViews; 81 | } 82 | 83 | @Override 84 | public void sortChildren(ViewGroup parent, List children) { 85 | final PointF comparisonPoint = getDistancePoint(parent, children); 86 | Collections.sort(children, new Comparator() { 87 | @Override 88 | public int compare(View left, View right) { 89 | double leftDistance = Math.abs(comparisonPoint.x - left.getX()) + Math.abs(comparisonPoint.y - left.getY()); 90 | double rightDistance = Math.abs(comparisonPoint.x - right.getX()) + Math.abs(comparisonPoint.y - right.getY()); 91 | if (reversed) { 92 | return Double.compare(rightDistance, leftDistance); 93 | } 94 | return Double.compare(leftDistance, rightDistance); 95 | } 96 | }); 97 | } 98 | 99 | @Override 100 | public PointF getDistancePoint(ViewGroup parent, List children) { 101 | PointF distancePoint; 102 | 103 | switch (corner) { 104 | case TOP_LEFT: 105 | distancePoint = new PointF(0, 0); 106 | break; 107 | case TOP_RIGHT: 108 | distancePoint = new PointF(parent.getWidth(), 0); 109 | break; 110 | case BOTTOM_LEFT: 111 | distancePoint = new PointF(0, parent.getHeight()); 112 | break; 113 | case BOTTOM_RIGHT: 114 | distancePoint = new PointF(parent.getWidth(), parent.getHeight()); 115 | break; 116 | default: 117 | throw new AssertionError("Must be a valid Corner argument type"); 118 | } 119 | 120 | return distancePoint; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/DefaultSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | 31 | public class DefaultSort extends SortFunction { 32 | 33 | private final long interObjectDelay; 34 | 35 | /** 36 | * Default sort; handles views with a provided offset delay 37 | * @param interObjectDelay (long) delay between object animations 38 | */ 39 | public DefaultSort(long interObjectDelay) { 40 | this.interObjectDelay = interObjectDelay; 41 | } 42 | 43 | @Override 44 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 45 | List childTimedViews = new ArrayList<>(); 46 | long currentTimeOffset = 0L; 47 | 48 | for (View childView : children) { 49 | childTimedViews.add(new SpruceTimedView(childView, currentTimeOffset)); 50 | currentTimeOffset += interObjectDelay; 51 | } 52 | 53 | return childTimedViews; 54 | } 55 | 56 | @Override 57 | public void sortChildren(ViewGroup parent, List children) { 58 | // Do nothing, as the original order is maintained 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/InlineSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class InlineSort extends CorneredSort { 35 | 36 | private final long interObjectDelay; 37 | private final boolean reversed; 38 | 39 | /** 40 | * Animate child views from side to side (based on the provided corner parameter). 41 | * 42 | * @param interObjectDelay long delay between objects 43 | * @param reversed boolean indicating if the selection is reversed 44 | * @param corner {@link com.willowtreeapps.spruce.sort.CorneredSort.Corner Corner} value to start from 45 | */ 46 | public InlineSort(long interObjectDelay, boolean reversed, Corner corner) { 47 | super(interObjectDelay, reversed, corner); 48 | this.interObjectDelay = interObjectDelay; 49 | this.reversed = reversed; 50 | } 51 | 52 | @Override 53 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 54 | List timedViews = new ArrayList<>(); 55 | long currentTimeOffset = 0; 56 | 57 | if (reversed) { 58 | Collections.reverse(children); 59 | } 60 | 61 | for (View view : children) { 62 | timedViews.add(new SpruceTimedView(view, currentTimeOffset)); 63 | currentTimeOffset += interObjectDelay; 64 | } 65 | 66 | return timedViews; 67 | } 68 | 69 | @Override 70 | public void sortChildren(ViewGroup parent, List children) { 71 | final PointF comparisonPoint = getDistancePoint(parent, children); 72 | Collections.sort(children, new Comparator() { 73 | @Override 74 | public int compare(View left, View right) { 75 | double leftHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(left)); 76 | double leftVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(left)); 77 | double rightHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(right)); 78 | double rightVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(right)); 79 | 80 | if (leftVerticalDistance < rightVerticalDistance || 81 | leftVerticalDistance == rightVerticalDistance && 82 | leftHorizontalDistance < rightHorizontalDistance) { 83 | return -1; 84 | } 85 | return 1; 86 | } 87 | }); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/LinearSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.List; 30 | 31 | public class LinearSort extends DistancedSort { 32 | 33 | private final Direction direction; 34 | 35 | public enum Direction { 36 | TOP_TO_BOTTOM, 37 | BOTTOM_TO_TOP, 38 | LEFT_TO_RIGHT, 39 | RIGHT_TO_LEFT 40 | } 41 | 42 | /** 43 | * Establishes the delay between object animations and their direction based on distance, 44 | * delay, and a value from the Direction enum 45 | * @param interObjectDelay delay between object animations 46 | * @param reversed flag to indicate if the animation should be reversed 47 | * @param direction enum value of the direction the animation should start from and end with 48 | */ 49 | public LinearSort(long interObjectDelay, boolean reversed, Direction direction) { 50 | super(interObjectDelay, reversed); 51 | if (direction == null) { 52 | throw new NullPointerException("Direction can't be null and must be of a valid type"); 53 | } 54 | this.direction = direction; 55 | } 56 | 57 | @Override 58 | public PointF getDistancePoint(ViewGroup parent, List children) { 59 | PointF point = super.getDistancePoint(parent, children); 60 | switch (direction) { 61 | case TOP_TO_BOTTOM: 62 | return new PointF(parent.getWidth() / 2.0F, 0F); 63 | case BOTTOM_TO_TOP: 64 | return new PointF(parent.getWidth() / 2.0F, parent.getHeight()); 65 | case LEFT_TO_RIGHT: 66 | return new PointF(0F, parent.getHeight() / 2.0F); 67 | case RIGHT_TO_LEFT: 68 | return new PointF(parent.getWidth(), parent.getHeight() / 2.0F); 69 | default: 70 | throw new AssertionError("Must be a valid Direction argument type"); 71 | } 72 | } 73 | 74 | @Override 75 | public double getDistanceBetweenPoints(PointF left, PointF right) { 76 | switch (direction) { 77 | case BOTTOM_TO_TOP: 78 | case TOP_TO_BOTTOM: 79 | left.x = 0F; 80 | right.x = 0F; 81 | break; 82 | case LEFT_TO_RIGHT: 83 | case RIGHT_TO_LEFT: 84 | left.y = 0F; 85 | right.y = 0F; 86 | break; 87 | } 88 | return Utils.euclideanDistance(left, right); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/RadialSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.List; 30 | 31 | public class RadialSort extends DistancedSort { 32 | 33 | private final Position position; 34 | 35 | public enum Position { 36 | TOP_LEFT, 37 | TOP_MIDDLE, 38 | TOP_RIGHT, 39 | LEFT, 40 | MIDDLE, 41 | RIGHT, 42 | BOTTOM_LEFT, 43 | BOTTOM_MIDDLE, 44 | BOTTOM_RIGHT 45 | } 46 | 47 | /** 48 | * Establishes the delay between object animations and their starting position based on distance, 49 | * delay, and a value from the Position enum 50 | * 51 | * @param interObjectDelay delay between object animations 52 | * @param reversed flag to indicate if the animation should be reversed 53 | * @param position enum value of the position the animation should start from 54 | */ 55 | public RadialSort(long interObjectDelay, boolean reversed, Position position) { 56 | super(interObjectDelay, reversed); 57 | if (position == null) { 58 | throw new NullPointerException("Position can't be null and must be a valid type"); 59 | } 60 | this.position = position; 61 | } 62 | 63 | @Override 64 | public PointF getDistancePoint(ViewGroup parent, List children) { 65 | PointF distancePoint; 66 | 67 | switch (position) { 68 | case TOP_LEFT: 69 | distancePoint = new PointF(0, 0); 70 | break; 71 | case TOP_MIDDLE: 72 | distancePoint = new PointF(parent.getWidth() / 2, 0); 73 | break; 74 | case TOP_RIGHT: 75 | distancePoint = new PointF(parent.getWidth(), 0); 76 | break; 77 | case LEFT: 78 | distancePoint = new PointF(0, parent.getHeight() / 2); 79 | break; 80 | case MIDDLE: 81 | distancePoint = new PointF(parent.getWidth() / 2, parent.getHeight() / 2); 82 | break; 83 | case RIGHT: 84 | distancePoint = new PointF(parent.getWidth(), parent.getHeight() / 2); 85 | break; 86 | case BOTTOM_LEFT: 87 | distancePoint = new PointF(0, parent.getHeight()); 88 | break; 89 | case BOTTOM_MIDDLE: 90 | distancePoint = new PointF(parent.getWidth() / 2, parent.getHeight()); 91 | break; 92 | case BOTTOM_RIGHT: 93 | distancePoint = new PointF(parent.getWidth(), parent.getHeight()); 94 | break; 95 | default: 96 | throw new AssertionError("Must be a valid Position argument type"); 97 | } 98 | 99 | return super.translate(distancePoint, children); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/RandomSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import java.util.ArrayList; 29 | import java.util.Collections; 30 | import java.util.List; 31 | 32 | public class RandomSort extends SortFunction { 33 | 34 | private final long interObjectDelay; 35 | 36 | /** 37 | * Random sort pattern that utilizes {@link Collections#shuffle(List) shuffle()} 38 | * 39 | * @param interObjectDelay 40 | */ 41 | public RandomSort(long interObjectDelay) { 42 | this.interObjectDelay = interObjectDelay; 43 | } 44 | 45 | @Override 46 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 47 | List timedViews = new ArrayList<>(); 48 | long currentTimeOffset = 0; 49 | 50 | for (View view : children) { 51 | timedViews.add(new SpruceTimedView(view, currentTimeOffset)); 52 | currentTimeOffset += interObjectDelay; 53 | } 54 | 55 | return timedViews; 56 | } 57 | 58 | @Override 59 | public void sortChildren(ViewGroup parent, List children) { 60 | Collections.shuffle(children); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/SnakeSort.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class SnakeSort extends CorneredSort { 35 | 36 | private final long interObjectDelay; 37 | private final boolean reversed; 38 | 39 | /** 40 | * Animate child views from side to side (based on the provided corner parameter), alternating left to right and right to left on each row. 41 | * 42 | * @param interObjectDelay long delay between objects 43 | * @param reversed boolean indicating if the selection is reversed 44 | * @param corner {@link com.willowtreeapps.spruce.sort.CorneredSort.Corner Corner} value to start from 45 | */ 46 | public SnakeSort(long interObjectDelay, boolean reversed, Corner corner) { 47 | super(interObjectDelay, reversed, corner); 48 | this.interObjectDelay = interObjectDelay; 49 | this.reversed = reversed; 50 | } 51 | 52 | @Override 53 | public List getViewListWithTimeOffsets(ViewGroup parent, List children) { 54 | final PointF comparisonPoint = getDistancePoint(parent, children); 55 | List timedViews = new ArrayList<>(); 56 | long currentTimeOffset = 0; 57 | 58 | // Calculate all possible vertical distances from the point of comparison. 59 | final List verticalDistances = new ArrayList<>(); 60 | for (View child: children) { 61 | float d = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(child)); 62 | if (!verticalDistances.contains(d)) { 63 | verticalDistances.add(d); 64 | } 65 | } 66 | 67 | // Sort these so we can find the row index by the vertical distance. 68 | Collections.sort(verticalDistances); 69 | 70 | Collections.sort(children, new Comparator() { 71 | @Override 72 | public int compare(View left, View right) { 73 | double leftHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(left)); 74 | double leftVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(left)); 75 | double rightHorizontalDistance = Utils.horizontalDistance(comparisonPoint, Utils.viewToPoint(right)); 76 | double rightVerticalDistance = Utils.verticalDistance(comparisonPoint, Utils.viewToPoint(right)); 77 | 78 | // Difference in vertical distance takes priority. 79 | if (leftVerticalDistance < rightVerticalDistance) { 80 | return -1; 81 | } else if (leftVerticalDistance > rightVerticalDistance) { 82 | return 1; 83 | } 84 | 85 | // If the are in the same row, find the row index. 86 | int row = verticalDistances.indexOf((float) leftVerticalDistance); 87 | if (leftHorizontalDistance < rightHorizontalDistance) { 88 | return row % 2 == 0 ? -1: 1; 89 | } else { 90 | return row % 2 == 0 ? 1: -1; 91 | } 92 | } 93 | }); 94 | 95 | if (reversed) { 96 | Collections.reverse(children); 97 | } 98 | 99 | for (View view : children) { 100 | timedViews.add(new SpruceTimedView(view, currentTimeOffset)); 101 | currentTimeOffset += interObjectDelay; 102 | } 103 | 104 | return timedViews; 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/SortFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import java.util.List; 29 | 30 | public abstract class SortFunction { 31 | 32 | /** 33 | * Sorts the children that will be animated in the view. 34 | * 35 | * @param parent ViewGroup parent of the collection of child views. 36 | * @param children The children to be sorted 37 | */ 38 | public abstract void sortChildren(ViewGroup parent, List children); 39 | 40 | /** 41 | * Get a list of SpruceTimedView 42 | * 43 | * @param parent ViewGroup parent of the collection of child views 44 | * @param children List of views to be animated 45 | * @return List of SpruceTimedView objects that contain the view and it's offset 46 | */ 47 | public abstract List getViewListWithTimeOffsets(ViewGroup parent, List children); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/SpruceTimedView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | 27 | public class SpruceTimedView { 28 | 29 | private final View view; 30 | private final long timeOffset; 31 | 32 | public SpruceTimedView(View view, long timeOffset) { 33 | this.view = view; 34 | this.timeOffset = timeOffset; 35 | } 36 | 37 | public View getView() { 38 | return view; 39 | } 40 | 41 | public long getTimeOffset() { 42 | return timeOffset; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/main/java/com/willowtreeapps/spruce/sort/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | 28 | import java.util.List; 29 | 30 | /** 31 | * Utility class for distance calculations and view to point conversion 32 | */ 33 | class Utils { 34 | 35 | /** 36 | * Get the euclidean distance between two points 37 | * 38 | * @param firstPoint PointF object 39 | * @param secondPoint PointF object 40 | * @return float value representing the distance in a straight line between two points 41 | */ 42 | public static float euclideanDistance(PointF firstPoint, PointF secondPoint) { 43 | return PointF.length(secondPoint.x - firstPoint.x, secondPoint.y - firstPoint.y); 44 | } 45 | 46 | /** 47 | * Get the horizontal, or x distance between two points 48 | * 49 | * @param firstPoint PointF object 50 | * @param secondPoint PointF object 51 | * @return float value representing the horizontal (or x) distance between two points 52 | */ 53 | public static float horizontalDistance(PointF firstPoint, PointF secondPoint) { 54 | return Math.abs(secondPoint.x - firstPoint.x); 55 | } 56 | 57 | /** 58 | * Get the vertical, or y distance between two points 59 | * 60 | * @param firstPoint PointF object 61 | * @param secondPoint PointF object 62 | * @return float value representing the vertical (or y) distance between two points 63 | */ 64 | public static float verticalDistance(PointF firstPoint, PointF secondPoint) { 65 | return Math.abs(secondPoint.y - firstPoint.y); 66 | } 67 | 68 | /** 69 | * Convert a view into it's coordinates as a Point 70 | * 71 | * @param view Object 72 | * @return PointF containing the x, y coordinates of the view 73 | */ 74 | public static PointF viewToPoint(View view) { 75 | return new PointF(Math.round(view.getX()), Math.round(view.getY())); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /lib/src/main/res/layout/anim_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 25 | 26 | 30 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/exclusion/ExclusionHelperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.exclusion; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.mockito.Mockito; 33 | import org.robolectric.RobolectricTestRunner; 34 | 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | 39 | @RunWith(RobolectricTestRunner.class) 40 | public class ExclusionHelperTest { 41 | 42 | private ViewGroup mockParent; 43 | 44 | @Before 45 | public void setup() { 46 | mockParent = Mockito.mock(ViewGroup.class); 47 | } 48 | 49 | @Test 50 | public void test_normal_exclusion_with_empty_list() { 51 | ExclusionHelper helper = new ExclusionHelper(); 52 | List idList = new ArrayList<>(); 53 | helper.initialize(idList, ExclusionHelper.NORMAL_MODE); 54 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size()); 55 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), 1); 56 | 57 | } 58 | 59 | @Test 60 | public void test_r_l_exclusion_with_empty_list() { 61 | ExclusionHelper helper = new ExclusionHelper(); 62 | List idList = new ArrayList<>(); 63 | helper.initialize(idList, ExclusionHelper.R_L_MODE); 64 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size()); 65 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), 1); 66 | 67 | } 68 | 69 | 70 | @Test 71 | public void test_normal_exclusion_success() { 72 | ExclusionHelper helper = new ExclusionHelper(); 73 | List idList = new ArrayList<>(); 74 | idList.add(2); 75 | idList.add(7); 76 | helper.initialize(idList, ExclusionHelper.NORMAL_MODE); 77 | ExclusionTestHelper.addViews(mockParent, 1, 2, 5, 7, 9); 78 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), idList.size()); 79 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size() + 1); 80 | 81 | for (View view : helper.filterViews(mockParent)) { 82 | Assert.assertFalse(idList.contains(view.getId())); 83 | } 84 | } 85 | 86 | @Test 87 | public void test_r_l_exclusion_success() { 88 | ExclusionHelper helper = new ExclusionHelper(); 89 | List idList = new ArrayList<>(); 90 | idList.add(5); 91 | idList.add(7); 92 | 93 | List positionList = new ArrayList<>(); 94 | positionList.add(2); 95 | positionList.add(3); 96 | 97 | 98 | helper.initialize(positionList, ExclusionHelper.R_L_MODE); 99 | ExclusionTestHelper.addViews(mockParent, 1, 2, 5, 7, 9); 100 | Assert.assertNotEquals(helper.filterViews(mockParent).size(), idList.size()); 101 | Assert.assertEquals(helper.filterViews(mockParent).size(), idList.size() + 1); 102 | 103 | for (View view : helper.filterViews(mockParent)) { 104 | Assert.assertFalse(idList.contains(view.getId())); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/exclusion/ExclusionTestHelper.java: -------------------------------------------------------------------------------- 1 | package com.willowtreeapps.spruce.exclusion; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import org.mockito.Mockito; 7 | 8 | public class ExclusionTestHelper { 9 | /** 10 | * This function creates a view groups according to the ids passed. 11 | * 12 | * @param viewGroup parent view group 13 | * @param ids id array. 14 | */ 15 | static void addViews(ViewGroup viewGroup, int... ids) { 16 | Mockito.when(viewGroup.getChildCount()).thenReturn(ids.length); 17 | 18 | for (int i = 0; i < ids.length; i++) { 19 | View mockView = Mockito.mock(View.class); 20 | Mockito.when(mockView.getId()).thenReturn(ids[i]); 21 | Mockito.when(viewGroup.getChildAt(i)).thenReturn(mockView); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/ContinuousSortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.mockito.Mockito; 33 | import org.robolectric.RobolectricTestRunner; 34 | 35 | import java.util.List; 36 | 37 | @RunWith(RobolectricTestRunner.class) 38 | public class ContinuousSortTest { 39 | 40 | private ViewGroup mockParent = Mockito.mock(ViewGroup.class); 41 | private List mockChildren; 42 | 43 | @Before 44 | public void setup() { 45 | mockChildren = TestHelper.setupMockChildren(); 46 | } 47 | 48 | @Test 49 | public void test_positive_inter_object_delay() { 50 | List resultViews = new ContinuousSort(/*interObjectDelay=*/1, 51 | /*reversed=*/false, 52 | RadialSort.Position.TOP_LEFT) 53 | .getViewListWithTimeOffsets(mockParent, mockChildren); 54 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 55 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 56 | Assert.assertEquals(1, resultViews.get(2).getTimeOffset()); 57 | } 58 | 59 | @Test 60 | public void test_positive_inter_object_delay_with_reversed() { 61 | List resultViews = new ContinuousSort(/*interObjectDelay=*/1, 62 | /*reversed=*/true, 63 | RadialSort.Position.TOP_LEFT) 64 | .getViewListWithTimeOffsets(mockParent, mockChildren); 65 | Assert.assertEquals(1, resultViews.get(0).getTimeOffset()); 66 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 67 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 68 | } 69 | 70 | @Test 71 | public void test_inter_object_delay_of_zero() { 72 | List resultViews = new ContinuousSort(/*interObjectDelay=*/0, 73 | /*reversed=*/false, 74 | RadialSort.Position.TOP_LEFT) 75 | .getViewListWithTimeOffsets(mockParent, mockChildren); 76 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 77 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 78 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 79 | } 80 | 81 | @Test 82 | public void test_negative_inter_object_delay() { 83 | List resultViews = new ContinuousSort(/*interObjectDelay=*/-1, 84 | /*reversed=*/false, 85 | RadialSort.Position.TOP_LEFT) 86 | .getViewListWithTimeOffsets(mockParent, mockChildren); 87 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 88 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 89 | Assert.assertEquals(-1, resultViews.get(2).getTimeOffset()); 90 | } 91 | 92 | @Test 93 | public void test_negative_inter_object_delay_with_reversed() { 94 | List resultViews = new ContinuousSort(/*interObjectDelay=*/-1, 95 | /*reversed=*/true, 96 | RadialSort.Position.TOP_LEFT) 97 | .getViewListWithTimeOffsets(mockParent, mockChildren); 98 | Assert.assertEquals(-1, resultViews.get(0).getTimeOffset()); 99 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 100 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 101 | } 102 | } -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/DefaultSortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.mockito.Mockito; 33 | import org.robolectric.RobolectricTestRunner; 34 | 35 | import java.util.List; 36 | 37 | @RunWith(RobolectricTestRunner.class) 38 | public class DefaultSortTest { 39 | 40 | private ViewGroup mockParent; 41 | private List mockChildren; 42 | 43 | @Before 44 | public void setup() { 45 | mockParent = Mockito.mock(ViewGroup.class); 46 | mockChildren = TestHelper.setupMockChildren(); 47 | } 48 | 49 | @Test 50 | public void test_inter_object_delay_of_views_is_set_on_default_sort() { 51 | List resultViews = new DefaultSort(/*interObjectDelay=*/1) 52 | .getViewListWithTimeOffsets(mockParent, mockChildren); 53 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 54 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 55 | Assert.assertEquals(2, resultViews.get(2).getTimeOffset()); 56 | } 57 | 58 | @Test 59 | public void test_inter_object_delay_of_zero() { 60 | List resultViews = new DefaultSort(/*interObjectDelay=*/0) 61 | .getViewListWithTimeOffsets(mockParent, mockChildren); 62 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 63 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 64 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 65 | } 66 | 67 | @Test 68 | public void test_negative_inter_object_delay() { 69 | List resultViews = new DefaultSort(/*interObjectDelay=*/-1) 70 | .getViewListWithTimeOffsets(mockParent, mockChildren); 71 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 72 | Assert.assertEquals(-1, resultViews.get(1).getTimeOffset()); 73 | Assert.assertEquals(-2, resultViews.get(2).getTimeOffset()); 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/DistancedSortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | 29 | import org.hamcrest.CoreMatchers; 30 | import org.junit.Assert; 31 | import org.junit.Before; 32 | import org.junit.Test; 33 | import org.junit.runner.RunWith; 34 | import org.mockito.Mockito; 35 | import org.robolectric.RobolectricTestRunner; 36 | 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | @RunWith(RobolectricTestRunner.class) 41 | public class DistancedSortTest { 42 | 43 | private ViewGroup mockParent; 44 | private List mockChildren; 45 | 46 | @Before 47 | public void setup() { 48 | mockParent = Mockito.mock(ViewGroup.class); 49 | mockChildren = TestHelper.setupMockChildren(); 50 | } 51 | 52 | @Test 53 | public void test_positive_inter_object_delay() { 54 | List resultViews = new DistancedSort(/*interObjectDelay=*/1, 55 | /*reversed=*/false) 56 | .getViewListWithTimeOffsets(mockParent, mockChildren); 57 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 58 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 59 | Assert.assertEquals(2, resultViews.get(2).getTimeOffset()); 60 | } 61 | 62 | @Test 63 | public void test_inter_object_delay_of_zero() { 64 | List resultViews = new DistancedSort(/*interObjectDelay=*/0, 65 | /*reversed=*/false) 66 | .getViewListWithTimeOffsets(mockParent, mockChildren); 67 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 68 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 69 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 70 | } 71 | 72 | @Test 73 | public void test_negative_inter_object_delay() { 74 | List resultViews = new DistancedSort(/*interObjectDelay=*/-1, 75 | /*reversed=*/false) 76 | .getViewListWithTimeOffsets(mockParent, mockChildren); 77 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 78 | Assert.assertEquals(-1, resultViews.get(1).getTimeOffset()); 79 | Assert.assertEquals(-2, resultViews.get(2).getTimeOffset()); 80 | } 81 | 82 | @Test 83 | public void test_get_distance_point_returns_a_point() { 84 | ViewGroup mockParent = Mockito.mock(ViewGroup.class); 85 | List mockChildren = new ArrayList<>(); 86 | mockChildren.add(Mockito.mock(View.class)); 87 | 88 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false); 89 | Assert.assertThat(distancedSort.getDistancePoint(mockParent, mockChildren), CoreMatchers.instanceOf(PointF.class)); 90 | } 91 | 92 | @Test 93 | public void test_translate_returns_a_point() { 94 | List mockChildren = new ArrayList<>(); 95 | mockChildren.add(Mockito.mock(View.class)); 96 | 97 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false); 98 | Assert.assertThat(distancedSort.translate(new PointF(0, 0), mockChildren), CoreMatchers.instanceOf(PointF.class)); 99 | } 100 | 101 | @Test 102 | public void test_get_distance_between_points_returns_a_double() { 103 | DistancedSort distancedSort = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false); 104 | PointF firstPoint = Mockito.mock(PointF.class); 105 | PointF secondPoint = Mockito.mock(PointF.class); 106 | 107 | Assert.assertThat(distancedSort.getDistanceBetweenPoints(firstPoint, secondPoint), CoreMatchers.instanceOf(Double.class)); 108 | } 109 | 110 | @Test 111 | public void test_get_view_list_with_time_offsets_returns_correct_number_of_children() { 112 | ViewGroup mockParent = Mockito.mock(ViewGroup.class); 113 | List mockChildren = new ArrayList<>(); 114 | for (int i = 0; i < 3; i++) { 115 | mockChildren.add(Mockito.mock(View.class)); 116 | } 117 | List resultViews = new DistancedSort(/*interObjectDelay=*/0, /*reversed=*/false).getViewListWithTimeOffsets(mockParent, mockChildren); 118 | Assert.assertEquals(mockChildren.size(), resultViews.size()); 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/InlineSortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.mockito.Mockito; 33 | import org.robolectric.RobolectricTestRunner; 34 | 35 | import java.util.List; 36 | 37 | @RunWith(RobolectricTestRunner.class) 38 | public class InlineSortTest { 39 | 40 | private ViewGroup mockParent; 41 | private List mockChildren; 42 | 43 | @Before 44 | public void setup() { 45 | mockParent = Mockito.mock(ViewGroup.class); 46 | mockChildren = TestHelper.setupMockChildren(); 47 | } 48 | 49 | @Test 50 | public void test_positive_inter_object_delay() { 51 | List resultViews = new InlineSort(/*interObjectDelay=*/1, 52 | /*reversed=*/false, 53 | CorneredSort.Corner.BOTTOM_RIGHT) 54 | .getViewListWithTimeOffsets(mockParent, mockChildren); 55 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 56 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 57 | Assert.assertEquals(2, resultViews.get(2).getTimeOffset()); 58 | } 59 | 60 | @Test 61 | public void test_inter_object_delay_of_zero() { 62 | List resultViews = new InlineSort(/*interObjectDelay=*/0, 63 | /*reversed=*/false, 64 | CorneredSort.Corner.BOTTOM_RIGHT) 65 | .getViewListWithTimeOffsets(mockParent, mockChildren); 66 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 67 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 68 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 69 | } 70 | 71 | @Test 72 | public void test_negative_inter_object_delay() { 73 | List resultViews = new InlineSort(/*interObjectDelay=*/-1, 74 | /*reversed=*/false, 75 | CorneredSort.Corner.BOTTOM_RIGHT) 76 | .getViewListWithTimeOffsets(mockParent, mockChildren); 77 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 78 | Assert.assertEquals(-1, resultViews.get(1).getTimeOffset()); 79 | Assert.assertEquals(-2, resultViews.get(2).getTimeOffset()); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/RandomSortTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.mockito.Mockito; 33 | import org.robolectric.RobolectricTestRunner; 34 | 35 | import java.util.List; 36 | 37 | @RunWith(RobolectricTestRunner.class) 38 | public class RandomSortTest { 39 | 40 | private ViewGroup mockParent = Mockito.mock(ViewGroup.class); 41 | private List mockChildren; 42 | 43 | @Before 44 | public void setup() { 45 | mockChildren = TestHelper.setupMockChildren(); 46 | } 47 | 48 | @Test 49 | public void test_positive_inter_object_delay() { 50 | List resultViews = new RandomSort(/*interObjectDelay=*/1) 51 | .getViewListWithTimeOffsets(mockParent, mockChildren); 52 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 53 | Assert.assertEquals(1, resultViews.get(1).getTimeOffset()); 54 | Assert.assertEquals(2, resultViews.get(2).getTimeOffset()); 55 | } 56 | 57 | @Test 58 | public void test_inter_object_delay_of_zero() { 59 | List resultViews = new RandomSort(/*interObjectDelay=*/0) 60 | .getViewListWithTimeOffsets(mockParent, mockChildren); 61 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 62 | Assert.assertEquals(0, resultViews.get(1).getTimeOffset()); 63 | Assert.assertEquals(0, resultViews.get(2).getTimeOffset()); 64 | } 65 | 66 | @Test 67 | public void test_negative_inter_object_delay() { 68 | List resultViews = new RandomSort(/*interObjectDelay=*/-1) 69 | .getViewListWithTimeOffsets(mockParent, mockChildren); 70 | Assert.assertEquals(0, resultViews.get(0).getTimeOffset()); 71 | Assert.assertEquals(-1, resultViews.get(1).getTimeOffset()); 72 | Assert.assertEquals(-2, resultViews.get(2).getTimeOffset()); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/ShadowPointF.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.graphics.PointF; 26 | 27 | import android.graphics.Point; 28 | 29 | import org.robolectric.annotation.Implements; 30 | import org.robolectric.annotation.Implementation; 31 | import org.robolectric.annotation.RealObject; 32 | 33 | /** 34 | * Shadow implementation of {@code PointF} 35 | */ 36 | @SuppressWarnings({"UnusedDeclaration"}) 37 | @Implements(PointF.class) 38 | public class ShadowPointF { 39 | @RealObject private PointF realPointF; 40 | 41 | public void __constructor__(float x, float y) { 42 | realPointF.x = x; 43 | realPointF.y = y; 44 | } 45 | 46 | public void __constructor__(Point src) { 47 | realPointF.x = src.x; 48 | realPointF.y = src.y; 49 | } 50 | 51 | @Implementation 52 | public void set(float x, float y) { 53 | realPointF.x = x; 54 | realPointF.y = y; 55 | } 56 | 57 | @Implementation 58 | public final void negate() { 59 | realPointF.x = -realPointF.x; 60 | realPointF.y = -realPointF.y; 61 | } 62 | 63 | @Implementation 64 | public final void offset(float dx, float dy) { 65 | realPointF.x += dx; 66 | realPointF.y += dy; 67 | } 68 | 69 | @Override @Implementation 70 | public boolean equals(Object object) { 71 | if (object == null) return false; 72 | if (this == object) return true; 73 | if (object.getClass() != PointF.class) return false; 74 | 75 | PointF that = (PointF) object; 76 | if (this.realPointF.x == that.x && this.realPointF.y == that.y) return true; 77 | 78 | return false; 79 | } 80 | 81 | @Override @Implementation 82 | public int hashCode() { 83 | return (int) (realPointF.x * 32713 + realPointF.y); 84 | } 85 | 86 | @Override @Implementation 87 | public String toString() { 88 | return "Point(" + realPointF.x + ", " + realPointF.y + ")"; 89 | } 90 | 91 | /** 92 | * Non-Android utility method for comparing a point to a well-known value 93 | * 94 | * @param x x 95 | * @param y y 96 | * @return this.x == x && this.y == y 97 | */ 98 | @Implementation 99 | public final boolean equals(float x, float y) { 100 | return realPointF.x == x && realPointF.y == y; 101 | } 102 | } -------------------------------------------------------------------------------- /lib/src/test/java/com/willowtreeapps/spruce/sort/TestHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Spruce 3 | * 4 | * Copyright (c) 2017 WillowTree, Inc. 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | */ 22 | 23 | package com.willowtreeapps.spruce.sort; 24 | 25 | import android.view.View; 26 | 27 | import org.mockito.Mockito; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | class TestHelper { 33 | 34 | static List setupMockChildren() { 35 | List mockChildren = new ArrayList<>(); 36 | float x = 0; 37 | float y = 0; 38 | for (int i = 0; i < 3; i++) { 39 | View mockView = Mockito.mock(View.class); 40 | Mockito.when(mockView.getX()).thenReturn(x); 41 | Mockito.when(mockView.getY()).thenReturn(y); 42 | mockChildren.add(mockView); 43 | x++; 44 | y++; 45 | } 46 | return mockChildren; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':lib', ':app' 2 | --------------------------------------------------------------------------------