├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── vpaliy.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── .travis.yml ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── vpaliy │ │ └── fabexploration │ │ ├── BaseFragment.java │ │ ├── Extensions.kt │ │ ├── MainActivity.java │ │ ├── NotifierTask.java │ │ ├── SeparatorView.java │ │ ├── dots │ │ └── DotsFragment.java │ │ ├── photo │ │ └── PhotoFragment.kt │ │ ├── player │ │ └── PlayerFragment.java │ │ ├── share │ │ └── ShareFragment.kt │ │ └── sheets │ │ ├── SheetAdapter.kt │ │ └── SheetFragment.kt │ └── res │ ├── drawable-nodpi │ └── city.jpg │ ├── drawable │ ├── drawer_icon_tint.xml │ ├── drawer_item.xml │ ├── empty.xml │ ├── github.xml │ ├── header.jpg │ ├── ic_arrow_back.xml │ ├── ic_close.xml │ ├── ic_facebook.xml │ ├── ic_fast_forward.xml │ ├── ic_fast_rewind.xml │ ├── ic_google.xml │ ├── ic_linkedin.xml │ ├── ic_overflow.xml │ ├── ic_pause.xml │ ├── ic_play.xml │ ├── ic_play_bottom.xml │ ├── ic_share.xml │ ├── ic_skip_next.xml │ ├── ic_skip_prev.xml │ ├── ic_star.xml │ ├── ic_twitter.xml │ ├── ic_volume_bottom.xml │ ├── ic_volume_high.xml │ ├── ic_volume_medium.xml │ ├── id.jpg │ ├── kodaline.jpg │ ├── morph_background.xml │ ├── photo_background.xml │ ├── ring.xml │ ├── rounded.xml │ ├── shape_thumb.xml │ └── sheet_background.xml │ ├── layout │ ├── adapter_sheet.xml │ ├── fragment_card.xml │ ├── fragment_dots.xml │ ├── fragment_photo.xml │ ├── fragment_player.xml │ ├── fragment_share.xml │ ├── fragment_sheets.xml │ ├── header.xml │ ├── layout_controls.xml │ ├── layout_dots.xml │ ├── layout_fab.xml │ ├── layout_photo.xml │ ├── layout_toolbar.xml │ └── main.xml │ ├── menu │ ├── drawer_list.xml │ └── main.xml │ ├── mipmap-nodpi │ └── icon.png │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── integers.xml │ ├── strings.xml │ └── styles.xml ├── art ├── dot.gif ├── dots_g.gif ├── player.gif └── sheets.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keys.jks └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/vpaliy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | kodaline 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: oraclejdk8 4 | 5 | android: 6 | components: 7 | - tools 8 | - build-tools-26.0.2 9 | - android-26 10 | 11 | before_cache: 12 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 13 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 14 | cache: 15 | directories: 16 | - $HOME/.gradle/caches/ 17 | - $HOME/.gradle/wrapper/ 18 | - $HOME/.android/build-cache -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Vasyl Paliy 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Material Motion 2 | [![Build Status](https://travis-ci.org/vpaliyX/Reveal-Animations.svg?branch=master)](https://travis-ci.org/vpaliy/Reveal-Animations) 3 | 4 | 5 | 6 | 7 | 8 | ## Or download the APK file [here](https://github.com/vpaliy/Material-Motion/releases/download/v1.0/app-release.apk) ## 9 | 10 | 11 | This repostiroy contains different reveal animations (will add more soon). 12 | 13 | If you decide you want to run the app: 14 | - clone the repository `git clone https://github.com/vpaliy/Reveal-Animations.git` 15 | - use the left drawer in order to navigate through the animations. (just swipe left) 16 | 17 | # Dots # 18 | ![](https://github.com/vpaliyX/FabExploration/blob/master/art/dot.gif) 19 | 20 | 21 | # Player # 22 | ![](https://github.com/vpaliyX/FabExploration/blob/master/art/player.gif) 23 | 24 | 25 | # Sheets # 26 | ![](https://github.com/vpaliyX/FabExploration/blob/master/art/sheets.gif) 27 | 28 | 29 | ## License ## 30 | 31 | `````` 32 | Apache License 33 | 34 | Copyright (c) 2018 Vasyl Paliy 35 | 36 | Licensed under the Apache License, Version 2.0 (the "License"); 37 | you may not use this file except in compliance with the License. 38 | You may obtain a copy of the License at 39 | 40 | http://www.apache.org/licenses/LICENSE-2.0 41 | 42 | Unless required by applicable law or agreed to in writing, software 43 | distributed under the License is distributed on an "AS IS" BASIS, 44 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | See the License for the specific language governing permissions and 46 | limitations under the License. 47 | `````` 48 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | signingConfigs { 7 | materialConfig { 8 | keyAlias 'key0' 9 | keyPassword 'materialmotion' 10 | storeFile file("$rootDir/keys.jks") 11 | storePassword 'materialmotion' 12 | } 13 | } 14 | compileSdkVersion 25 15 | buildToolsVersion "26.0.2" 16 | defaultConfig { 17 | applicationId "com.vpaliy.fabexploration" 18 | minSdkVersion 16 19 | targetSdkVersion 25 20 | versionCode 1 21 | versionName "1.0" 22 | vectorDrawables.useSupportLibrary = true 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | buildTypes { 29 | release { 30 | debuggable false 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | signingConfig signingConfigs.materialConfig 34 | } 35 | } 36 | lintOptions { 37 | abortOnError false 38 | disable 'RestrictedApi' 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation('com.github.ozodrukh:CircularReveal:2.0.1@aar') { 44 | transitive = true 45 | } 46 | implementation 'com.android.support:cardview-v7:25.4.0' 47 | implementation 'com.github.vpaliyX:android-extensions:-SNAPSHOT' 48 | implementation 'com.github.bumptech.glide:glide:4.3.1' 49 | implementation 'com.github.ohoussein.playpauseview:playpauseview:1.0.1' 50 | implementation 'com.andkulikov:transitionseverywhere:1.7.4' 51 | implementation 'com.jakewharton:butterknife:8.8.1' 52 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 53 | implementation 'com.android.support:appcompat-v7:25.4.0' 54 | implementation 'com.android.support:design:25.4.0' 55 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 56 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 57 | } 58 | repositories { 59 | mavenCentral() 60 | } 61 | -------------------------------------------------------------------------------- /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 /home/vpaliy/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.graphics.Path; 5 | import android.graphics.PathMeasure; 6 | import android.os.Bundle; 7 | import android.support.annotation.IntegerRes; 8 | import android.support.annotation.LayoutRes; 9 | import android.support.annotation.Nullable; 10 | import android.support.v4.app.Fragment; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | import butterknife.ButterKnife; 16 | import butterknife.Unbinder; 17 | 18 | public abstract class BaseFragment extends Fragment { 19 | 20 | private Unbinder unbinder; 21 | 22 | @Override 23 | public void onDestroy() { 24 | super.onDestroy(); 25 | if (unbinder != null) { 26 | unbinder.unbind(); 27 | } 28 | } 29 | 30 | @Nullable 31 | @Override 32 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 33 | View root = inflater.inflate(mainRes(), container, false); 34 | setRetainInstance(true); 35 | unbinder = ButterKnife.bind(this, root); 36 | return root; 37 | } 38 | 39 | @LayoutRes 40 | protected abstract int mainRes(); 41 | 42 | protected Path createArcPath(View view, float endX, float endY, float radius) { 43 | Path arcPath = new Path(); 44 | float startX = view.getTranslationX(); 45 | float startY = view.getTranslationY(); 46 | float midX = startX + ((endX - startX) / 2); 47 | float midY = startY + ((endY - startY) / 2); 48 | float xDiff = midX - startX; 49 | float yDiff = midY - startY; 50 | 51 | double angle = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90; 52 | double angleRadians = Math.toRadians(angle); 53 | 54 | float pointX = (float) (midX + radius * Math.cos(angleRadians)); 55 | float pointY = (float) (midY + radius * Math.sin(angleRadians)); 56 | 57 | arcPath.moveTo(startX, startY); 58 | arcPath.cubicTo(startX, startY, pointX, pointY, endX, endY); 59 | return arcPath; 60 | } 61 | 62 | protected int duration(@IntegerRes int resource) { 63 | return getResources().getInteger(resource); 64 | } 65 | 66 | protected class ArcListener implements ValueAnimator.AnimatorUpdateListener { 67 | 68 | private float point[] = new float[2]; 69 | private PathMeasure pathMeasure; 70 | private View target; 71 | 72 | public ArcListener(Path path, View target) { 73 | this.pathMeasure = new PathMeasure(path, false); 74 | this.target = target; 75 | } 76 | 77 | @Override 78 | public void onAnimationUpdate(ValueAnimator animation) { 79 | final float value = animation.getAnimatedFraction(); 80 | // Gets the point at the fractional path length 81 | pathMeasure.getPosTan(pathMeasure.getLength() * value, point, null); 82 | 83 | // Sets view location to the above point 84 | target.setTranslationX(point[0]); 85 | target.setTranslationY(point[1]); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.animation.AnimatorSet 6 | import android.animation.ValueAnimator 7 | import android.content.Context 8 | import android.support.annotation.DimenRes 9 | import android.support.v4.app.Fragment 10 | import android.support.v4.view.ViewCompat 11 | import android.util.Log 12 | import android.view.View 13 | import android.view.animation.Animation 14 | 15 | fun View.getHeightAnimator(endHeight: Int, duration: Long = 200): ValueAnimator { 16 | val heightAnimator = ValueAnimator.ofInt(height, endHeight) 17 | heightAnimator.addUpdateListener { 18 | val value = it.animatedValue as Int 19 | layoutParams.height = value 20 | requestLayout() 21 | } 22 | heightAnimator.duration = duration 23 | return heightAnimator 24 | } 25 | 26 | fun View.getWidthAnimator(endWidth: Int, duration: Long = 200): ValueAnimator { 27 | val widthAnimator = ValueAnimator.ofInt(width, endWidth) 28 | widthAnimator.addUpdateListener { 29 | val value = it.animatedValue as Int 30 | layoutParams.width = value 31 | requestLayout() 32 | } 33 | widthAnimator.duration = duration 34 | return widthAnimator 35 | } 36 | 37 | fun View.setHeight(endHeight: Int) { 38 | layoutParams.height = endHeight 39 | requestLayout() 40 | } 41 | 42 | fun AnimatorSet.playWith(vararg items: Animator) = apply { 43 | playTogether(items.toMutableList()) 44 | } 45 | 46 | fun Animator.playWith(animator: Animator): Animator { 47 | if (animator is AnimatorSet) 48 | return animator.playWith(this) 49 | else if (this is AnimatorSet) 50 | return playWith(animator) 51 | return AnimatorSet().playWith(this, animator) 52 | } 53 | 54 | fun Fragment.getDimension(@DimenRes id: Int) = resources.getDimension(id) 55 | 56 | infix fun View.setElevation(@DimenRes id: Int) { 57 | ViewCompat.setElevation(this, context.getDimensFloat(id)) 58 | } 59 | 60 | fun Any.log(message: Any, tag: String = this.javaClass.name) { 61 | Log.d(tag, message.toString()) 62 | } 63 | 64 | fun View.click(function: () -> Unit) = setOnClickListener { function() } 65 | 66 | fun View.halfWidth() = width / 2f 67 | 68 | fun View.halfHeight() = height / 2f 69 | 70 | infix fun Boolean.then(value: T) = if (this) value else null 71 | 72 | infix fun Context.getDimens(id: Int) = resources.getDimensionPixelOffset(id) 73 | 74 | infix fun Context.getDimensFloat(id: Int) = resources.getDimension(id) 75 | 76 | fun Animator.onEnd(callback: (Animator?) -> Unit) = Keeper(this).onEnd(callback) 77 | 78 | fun Animator.onStart(callback: (Animator?) -> Unit) = Keeper(this).onStart(callback) 79 | 80 | fun Animator.onCancel(callback: (Animator?) -> Unit) = Keeper(this).onCancel(callback) 81 | 82 | fun Animator.onRepeat(callback: (Animator?) -> Unit) = Keeper(this).onRepeat(callback) 83 | 84 | class Keeper(private val animator: Animator) { 85 | private val executor = ListenerExecutor() 86 | 87 | init { 88 | animator.addListener(executor) 89 | } 90 | 91 | fun onEnd(callback: (Animator?) -> Unit) = apply { 92 | executor.end = callback 93 | } 94 | 95 | fun onStart(callback: (Animator?) -> Unit) = apply { 96 | executor.start = callback 97 | } 98 | 99 | fun onCancel(callback: (Animator?) -> Unit) = apply { 100 | executor.cancel = callback 101 | } 102 | 103 | fun onRepeat(callback: (Animator?) -> Unit) = apply { 104 | executor.repeat = callback 105 | } 106 | 107 | fun animator() = animator 108 | } 109 | 110 | 111 | private class ListenerExecutor : Animator.AnimatorListener { 112 | var end: ((Animator?) -> Unit)? = null 113 | var start: ((Animator?) -> Unit)? = null 114 | var cancel: ((Animator?) -> Unit)? = null 115 | var repeat: ((Animator?) -> Unit)? = null 116 | 117 | override fun onAnimationCancel(animation: Animator?) { 118 | cancel?.invoke(animation) 119 | } 120 | 121 | override fun onAnimationEnd(animation: Animator?) { 122 | end?.invoke(animation) 123 | } 124 | 125 | override fun onAnimationRepeat(animation: Animator?) { 126 | repeat?.invoke(animation) 127 | } 128 | 129 | override fun onAnimationStart(animation: Animator?) { 130 | start?.invoke(animation) 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.Uri; 8 | import android.preference.PreferenceManager; 9 | import android.support.design.widget.NavigationView; 10 | import android.support.v4.app.Fragment; 11 | import android.support.v4.view.MenuItemCompat; 12 | import android.support.v4.view.ViewCompat; 13 | import android.support.v4.widget.DrawerLayout; 14 | import android.support.v7.app.AppCompatActivity; 15 | import android.os.Bundle; 16 | import android.view.MenuItem; 17 | import android.view.View; 18 | import android.view.WindowManager; 19 | import android.view.animation.LinearInterpolator; 20 | import android.view.animation.OvershootInterpolator; 21 | import android.widget.ImageView; 22 | import android.widget.Switch; 23 | import android.widget.TextView; 24 | 25 | import com.bumptech.glide.Glide; 26 | import com.vpaliy.fabexploration.dots.DotsFragment; 27 | import com.vpaliy.fabexploration.photo.PhotoFragment; 28 | import com.vpaliy.fabexploration.player.PlayerFragment; 29 | import com.vpaliy.fabexploration.share.ShareFragment; 30 | import com.vpaliy.fabexploration.sheets.SheetFragment; 31 | 32 | import butterknife.ButterKnife; 33 | import butterknife.BindView; 34 | 35 | public class MainActivity extends AppCompatActivity 36 | implements SharedPreferences.OnSharedPreferenceChangeListener { 37 | 38 | private static final String WARNING_KEY = "enabled:warning"; 39 | 40 | @BindView(R.id.drawer) 41 | protected DrawerLayout drawer; 42 | 43 | @BindView(R.id.navigation) 44 | protected NavigationView navigation; 45 | 46 | @BindView(R.id.message) 47 | protected TextView message; 48 | 49 | private SharedPreferences preferences; 50 | private NotifierTask notifierTask; 51 | private boolean enabledWarning = false; 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | setContentView(R.layout.main); 57 | ButterKnife.bind(this); 58 | setupWarning(); 59 | setUpDrawer(); 60 | ViewCompat.setElevation(message, getResources(). 61 | getDimensionPixelOffset(R.dimen.message_elevation)); 62 | loadFragment(new PlayerFragment()); 63 | 64 | } 65 | 66 | private void setUpDrawer() { 67 | drawer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE 68 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 69 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 70 | 71 | final ImageView header = (ImageView) navigation.getHeaderView(0); 72 | Glide.with(this) 73 | .asDrawable() 74 | .load(R.drawable.header) 75 | .into(header); 76 | navigation.setNavigationItemSelectedListener(item -> { 77 | drawer.closeDrawers(); 78 | switch (item.getItemId()) { 79 | case R.id.player: 80 | getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 81 | loadFragment(new PlayerFragment()); 82 | return true; 83 | case R.id.dots: 84 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 85 | loadFragment(new DotsFragment()); 86 | return true; 87 | case R.id.sheets: 88 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 89 | loadFragment(new SheetFragment()); 90 | return true; 91 | case R.id.photo: 92 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 93 | loadFragment(new PhotoFragment()); 94 | return true; 95 | case R.id.share: 96 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 97 | loadFragment(new ShareFragment()); 98 | return true; 99 | case R.id.git: 100 | final Intent showGitHub = new Intent(Intent.ACTION_VIEW, 101 | Uri.parse(getString(R.string.GitHub))); 102 | startActivity(showGitHub); 103 | return true; 104 | } 105 | return false; 106 | }); 107 | } 108 | 109 | private void setupWarning() { 110 | preferences = PreferenceManager.getDefaultSharedPreferences(this); 111 | preferences.registerOnSharedPreferenceChangeListener(this); 112 | onSharedPreferenceChanged(preferences, WARNING_KEY); 113 | final MenuItem item = navigation.getMenu().findItem(R.id.enable_message); 114 | Switch view = (Switch) MenuItemCompat.getActionView(item); 115 | view.setChecked(preferences.getBoolean(WARNING_KEY, false)); 116 | view.setOnCheckedChangeListener((button, isChecked) -> { 117 | if (enabledWarning != isChecked) { 118 | preferences.edit() 119 | .putBoolean(WARNING_KEY, isChecked) 120 | .apply(); 121 | } 122 | }); 123 | } 124 | 125 | @Override 126 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 127 | if (key.equals(WARNING_KEY)) { 128 | final boolean isChanged = sharedPreferences.getBoolean(key, enabledWarning); 129 | if (isChanged != enabledWarning) { 130 | enabledWarning = isChanged; 131 | showWarning(); 132 | } 133 | } 134 | } 135 | 136 | @Override 137 | protected void onStop() { 138 | super.onStop(); 139 | if (preferences != null) { 140 | preferences.unregisterOnSharedPreferenceChangeListener(this); 141 | } 142 | } 143 | 144 | @Override 145 | protected void onStart() { 146 | super.onStart(); 147 | if (preferences != null) { 148 | preferences.registerOnSharedPreferenceChangeListener(this); 149 | } 150 | } 151 | 152 | private void loadFragment(Fragment fragment) { 153 | showWarning(); 154 | if (fragment != null) { 155 | getSupportFragmentManager().beginTransaction() 156 | .replace(R.id.frame, fragment) 157 | .commit(); 158 | } 159 | } 160 | 161 | private void showWarning() { 162 | if (notifierTask != null) { 163 | notifierTask.cancel(); 164 | } 165 | message.setText(R.string.drawer_warning); 166 | message.setScaleX(0f); 167 | message.setScaleY(0f); 168 | if (enabledWarning) { 169 | message.animate() 170 | .scaleX(1) 171 | .scaleY(1) 172 | .setStartDelay(500) 173 | .setDuration(300) 174 | .setInterpolator(new OvershootInterpolator()) 175 | .setListener(new AnimatorListenerAdapter() { 176 | @Override 177 | public void onAnimationEnd(Animator animation) { 178 | super.onAnimationEnd(animation); 179 | notifierTask = new NotifierTask.Starter(message, 3) 180 | .setCallback(() -> message.post(() -> 181 | message.animate() 182 | .scaleY(0) 183 | .scaleX(0) 184 | .setStartDelay(0) 185 | .setDuration(200) 186 | .setListener(null) 187 | .setInterpolator(new LinearInterpolator()) 188 | .start())) 189 | .setPeriod(1000) 190 | .setDelay(1000) 191 | .start(); 192 | } 193 | }).start(); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/NotifierTask.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration; 2 | 3 | import android.content.Context; 4 | import android.widget.TextView; 5 | 6 | import java.util.Timer; 7 | import java.util.TimerTask; 8 | 9 | @SuppressWarnings("WeakerAccess") 10 | public class NotifierTask extends TimerTask { 11 | private volatile int count; 12 | private Callback callback; 13 | private final TextView target; 14 | private final Context context; 15 | private final String message; 16 | 17 | private NotifierTask(Starter starter) { 18 | this.count = starter.count; 19 | this.target = starter.target; 20 | this.callback = starter.callback; 21 | this.context = target.getContext(); 22 | this.message = target.getText().toString(); 23 | } 24 | 25 | @Override 26 | public void run() { 27 | if (count < 0) return; 28 | if (count == 0) { 29 | cancel(); 30 | if (callback != null) { 31 | callback.onFinished(); 32 | } 33 | } else notifyTarget(); 34 | count--; 35 | } 36 | 37 | private void notifyTarget() { 38 | target.post(() -> target.setText(buildNotification())); 39 | } 40 | 41 | private String buildNotification() { 42 | return message + "\n" + context. 43 | getString(R.string.disappear_message) + ":" + count; 44 | } 45 | 46 | public static class Starter { 47 | private final TextView target; 48 | private final int count; 49 | private long delay; 50 | private long period; 51 | private Callback callback; 52 | 53 | public Starter(final TextView target, final int count) { 54 | this.target = target; 55 | this.count = count; 56 | } 57 | 58 | public Starter setDelay(long delay) { 59 | this.delay = delay; 60 | return this; 61 | } 62 | 63 | public Starter setCallback(Callback callback) { 64 | this.callback = callback; 65 | return this; 66 | } 67 | 68 | public Starter setPeriod(long period) { 69 | this.period = period; 70 | return this; 71 | } 72 | 73 | public NotifierTask start() { 74 | Timer timer = new Timer(); 75 | final NotifierTask task = new NotifierTask(this); 76 | timer.schedule(task, delay, period); 77 | return task; 78 | } 79 | } 80 | 81 | interface Callback { 82 | void onFinished(); 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/SeparatorView.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | 10 | public class SeparatorView extends View { 11 | 12 | private Paint linePaint; 13 | 14 | public SeparatorView(Context context) { 15 | super(context); 16 | init(); 17 | } 18 | 19 | public SeparatorView(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | init(); 22 | } 23 | 24 | private void init() { 25 | linePaint = new Paint(); 26 | linePaint.setAlpha(50); 27 | linePaint.setColor(Color.BLACK); 28 | linePaint.setStrokeWidth(10); 29 | } 30 | 31 | @Override 32 | protected void onDraw(Canvas canvas) { 33 | super.onDraw(canvas); 34 | canvas.drawLine(getWidth() / 10, 0, getWidth() / 10, getHeight(), linePaint); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/dots/DotsFragment.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.dots; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.animation.ValueAnimator; 8 | import android.graphics.Path; 9 | import android.graphics.drawable.GradientDrawable; 10 | import android.os.Bundle; 11 | import android.support.constraint.ConstraintLayout; 12 | import android.support.design.widget.FloatingActionButton; 13 | import android.support.v4.content.ContextCompat; 14 | import android.support.v4.util.ArrayMap; 15 | import android.support.v4.view.ViewCompat; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.view.animation.OvershootInterpolator; 19 | import android.widget.ImageView; 20 | 21 | import com.vpaliy.fabexploration.BaseFragment; 22 | import com.vpaliy.fabexploration.R; 23 | 24 | import java.util.List; 25 | 26 | import io.codetail.animation.ViewAnimationUtils; 27 | import butterknife.BindView; 28 | import butterknife.BindViews; 29 | import butterknife.OnClick; 30 | 31 | import android.support.annotation.Nullable; 32 | 33 | public class DotsFragment extends BaseFragment { 34 | 35 | @BindViews({R.id.first, R.id.second, R.id.third}) 36 | protected List dots; 37 | 38 | @BindView(R.id.parent) 39 | protected ViewGroup parent; 40 | 41 | @BindView(R.id.background) 42 | protected View background; 43 | 44 | @BindView(R.id.topPanel) 45 | protected View topPanel; 46 | 47 | @BindView(R.id.top) 48 | protected ViewGroup top; 49 | 50 | @BindView(R.id.close) 51 | protected ImageView close; 52 | 53 | @BindView(R.id.root) 54 | protected View root; 55 | 56 | private FloatingActionButton lastDot; 57 | 58 | private ArrayMap colors; 59 | 60 | private int color; 61 | private boolean isFolded; 62 | private boolean finished = true; 63 | 64 | @Override 65 | protected int mainRes() { 66 | return R.layout.fragment_dots; 67 | } 68 | 69 | @Override 70 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 71 | super.onViewCreated(view, savedInstanceState); 72 | if (view != null) { 73 | topPanel.post(() -> topPanel.setVisibility(View.GONE)); 74 | close.post(() -> close.setVisibility(View.GONE)); 75 | color = ContextCompat.getColor(getContext(), R.color.colorAccent); 76 | int firstColor = ContextCompat.getColor(getContext(), R.color.color_dot_first); 77 | int secondColor = ContextCompat.getColor(getContext(), R.color.color_dot_second); 78 | colors = new ArrayMap<>(); 79 | colors.put(color, secondColor); 80 | colors.put(firstColor, color); 81 | colors.put(secondColor, firstColor); 82 | } 83 | } 84 | 85 | @OnClick({R.id.first, R.id.third}) 86 | public void revealSides(FloatingActionButton dot) { 87 | if (finished) { 88 | finished = false; 89 | lastDot = dot; 90 | float deltaX = topPanel.getWidth() / 2 - dot.getX() - dot.getWidth() / 2; 91 | float deltaY = topPanel.getHeight() / 2 - dot.getY() - dot.getHeight() / 2; 92 | deltaY -= topPanel.getHeight() / 2 + getResources().getDimension(R.dimen.morph_radius) / 4; 93 | Path arcPath = createArcPath(dot, deltaX, deltaY, -deltaX); 94 | ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1); 95 | pathAnimator.addUpdateListener(new ArcListener(arcPath, dot)); 96 | int dotColor = dot.getBackgroundTintList().getDefaultColor(); 97 | topPanel.setBackgroundColor(dotColor); 98 | if (dotColor == color) { 99 | backgroundReveal().start(); 100 | } 101 | pathAnimator.addListener(new AnimatorListenerAdapter() { 102 | @Override 103 | public void onAnimationEnd(Animator animation) { 104 | super.onAnimationEnd(animation); 105 | Animator animator = createRevealAnimator(dot, 0); 106 | finish(animator); 107 | animator.start(); 108 | runCloseAnimation(); 109 | } 110 | }); 111 | AnimatorSet animatorSet = morphParent(duration(R.integer.reveal_duration)); 112 | animatorSet.play(pathAnimator); 113 | addScaleAnimation(duration(R.integer.short_delay), duration(R.integer.fade_duration), animatorSet); 114 | animatorSet.start(); 115 | } 116 | } 117 | 118 | private void runCloseAnimation() { 119 | close.setVisibility(View.VISIBLE); 120 | close.setAlpha(0f); 121 | close.setRotation(0f); 122 | close.animate() 123 | .alpha(1f) 124 | .setDuration(duration(R.integer.close_duration)) 125 | .start(); 126 | close.animate() 127 | .rotation(-360) 128 | .setDuration(duration(R.integer.close_duration)) 129 | .setStartDelay(duration(R.integer.medium_delay)) 130 | .start(); 131 | } 132 | 133 | @OnClick(R.id.topPanel) 134 | public void conceal() { 135 | if (!finished) return; 136 | close.setVisibility(View.INVISIBLE); 137 | finished = false; 138 | if (lastDot.getId() == R.id.second) { 139 | int height = top.getHeight(); 140 | ValueAnimator heightAnimation = ValueAnimator.ofInt(top.getHeight(), parent.getHeight()); 141 | heightAnimation.addUpdateListener(valueAnimator -> { 142 | int val = (Integer) valueAnimator.getAnimatedValue(); 143 | ViewGroup.LayoutParams layoutParams = top.getLayoutParams(); 144 | layoutParams.height = val; 145 | top.setLayoutParams(layoutParams); 146 | }); 147 | heightAnimation.addListener(new AnimatorListenerAdapter() { 148 | @Override 149 | public void onAnimationEnd(Animator animation) { 150 | super.onAnimationEnd(animation); 151 | Animator animator = createRevealAnimator(lastDot, 0); 152 | animator.addListener(new AnimatorListenerAdapter() { 153 | @Override 154 | public void onAnimationEnd(Animator animation) { 155 | lastDot.setVisibility(View.VISIBLE); 156 | topPanel.setVisibility(View.GONE); 157 | top.getLayoutParams().height = height; 158 | AnimatorSet animatorSet = morphParent(duration(R.integer.conceal_duration)); 159 | animatorSet.setDuration(duration(R.integer.conceal_duration) / 2); 160 | addScaleAnimation(duration(R.integer.short_delay), 161 | duration(R.integer.fade_in_duration), 162 | animatorSet); 163 | animatorSet.start(); 164 | finished = true; 165 | isFolded = !isFolded; 166 | } 167 | }); 168 | animator.start(); 169 | } 170 | }); 171 | heightAnimation.setDuration(duration(R.integer.conceal_duration)); 172 | heightAnimation.start(); 173 | return; 174 | } 175 | 176 | Animator animator = createRevealAnimator(lastDot, 0); 177 | animator.addListener(new AnimatorListenerAdapter() { 178 | @Override 179 | public void onAnimationEnd(Animator animation) { 180 | super.onAnimationEnd(animation); 181 | lastDot.setVisibility(View.VISIBLE); 182 | topPanel.setVisibility(View.GONE); 183 | Path arcPath = createArcPath(lastDot, 0, 0, lastDot.getTranslationX()); 184 | ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1); 185 | pathAnimator.addUpdateListener(new ArcListener(arcPath, lastDot)); 186 | AnimatorSet animatorSet = morphParent(duration(R.integer.conceal_duration)); 187 | animatorSet.play(pathAnimator); 188 | addScaleAnimation(duration(R.integer.long_delay), 189 | duration(R.integer.fade_in_duration), animatorSet); 190 | finish(animatorSet); 191 | animatorSet.start(); 192 | } 193 | }); 194 | animator.start(); 195 | } 196 | 197 | @OnClick(R.id.second) 198 | public void revealSecond(FloatingActionButton dot) { 199 | if (finished) { 200 | finished = false; 201 | lastDot = dot; 202 | ViewGroup.LayoutParams params = top.getLayoutParams(); 203 | int height = params.height; 204 | params.height = ConstraintLayout.LayoutParams.MATCH_PARENT; 205 | top.setLayoutParams(params); 206 | topPanel.setBackgroundColor(dot.getBackgroundTintList().getDefaultColor()); 207 | top.post(() -> { 208 | Animator animator = createRevealAnimator(lastDot, 0); 209 | int dotColor = dot.getBackgroundTintList().getDefaultColor(); 210 | if (dotColor == color) { 211 | backgroundReveal().start(); 212 | } 213 | AnimatorSet animatorSet = morphParent(duration(R.integer.reveal_duration)); 214 | animatorSet.play(animator); 215 | addScaleAnimation(0, duration(R.integer.short_delay), animatorSet); 216 | animatorSet.addListener(new AnimatorListenerAdapter() { 217 | @Override 218 | public void onAnimationEnd(Animator animation) { 219 | super.onAnimationEnd(animation); 220 | ValueAnimator heightAnimation = ValueAnimator.ofInt(top.getHeight(), height); 221 | heightAnimation.addUpdateListener(valueAnimator -> { 222 | int val = (Integer) valueAnimator.getAnimatedValue(); 223 | ViewGroup.LayoutParams layoutParams = top.getLayoutParams(); 224 | layoutParams.height = val; 225 | top.setLayoutParams(layoutParams); 226 | }); 227 | heightAnimation.setDuration(duration(R.integer.reveal_duration) / 2); 228 | finish(heightAnimation); 229 | heightAnimation.start(); 230 | runCloseAnimation(); 231 | } 232 | }); 233 | animatorSet.start(); 234 | }); 235 | } 236 | } 237 | 238 | private void finish(Animator animator) { 239 | animator.addListener(new AnimatorListenerAdapter() { 240 | @Override 241 | public void onAnimationEnd(Animator animation) { 242 | super.onAnimationEnd(animation); 243 | isFolded = !isFolded; 244 | finished = !finished; 245 | } 246 | }); 247 | } 248 | 249 | private Animator createRevealAnimator(FloatingActionButton dot, float offsetY) { 250 | ViewCompat.setElevation(dot, 0); 251 | dot.setVisibility(View.INVISIBLE); 252 | lastDot = dot; 253 | int cx = (int) (dot.getX() + dot.getHeight() / 2); 254 | int cy = (int) (dot.getY() + dot.getHeight() / 2 + offsetY); 255 | int w = topPanel.getWidth(); 256 | int h = topPanel.getHeight(); 257 | final int endRadius = !isFolded ? (int) Math.hypot(w, h) : dot.getHeight() / 2; 258 | final int startRadius = isFolded ? (int) Math.hypot(w, h) : dot.getHeight() / 2; 259 | topPanel.setVisibility(View.VISIBLE); 260 | Animator animator = ViewAnimationUtils.createCircularReveal(topPanel, cx, cy, startRadius, endRadius); 261 | animator.setDuration(duration(R.integer.reveal_duration)); 262 | return animator; 263 | } 264 | 265 | private Animator backgroundReveal() { 266 | root.setBackgroundColor(color); 267 | background.setBackgroundColor(color = colors.get(color)); 268 | int cx = (int) (parent.getX() + parent.getWidth() / 2); 269 | int cy = (int) (parent.getY() + parent.getHeight() / 2); 270 | int w = background.getWidth(); 271 | int h = background.getHeight(); 272 | Animator animator = ViewAnimationUtils.createCircularReveal(background, cx, cy, parent.getHeight() / 2, (int) Math.hypot(w, h)); 273 | animator.setDuration(duration(R.integer.reveal_duration) * 2); 274 | return animator; 275 | } 276 | 277 | private void addScaleAnimation(int startDelay, int duration, AnimatorSet set) { 278 | final int start = !isFolded ? 1 : 0; 279 | final int end = ~start & 0x1; 280 | AnimatorSet buttonSet = new AnimatorSet(); 281 | for (int index = 0; index < dots.size(); index++) { 282 | FloatingActionButton tempDot = dots.get(index); 283 | if (tempDot.getId() != lastDot.getId()) { 284 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(tempDot, View.SCALE_X, start, end); 285 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(tempDot, View.SCALE_Y, start, end); 286 | ObjectAnimator fade = ObjectAnimator.ofFloat(tempDot, View.ALPHA, start, end); 287 | scaleX.setStartDelay(startDelay); 288 | scaleY.setStartDelay(startDelay); 289 | scaleX.setInterpolator(new OvershootInterpolator(2)); 290 | scaleY.setInterpolator(new OvershootInterpolator(2)); 291 | fade.setStartDelay(startDelay); 292 | buttonSet.playTogether(scaleX, scaleY, fade); 293 | } 294 | } 295 | buttonSet.setDuration(duration); 296 | set.playTogether(buttonSet); 297 | } 298 | 299 | private AnimatorSet morphParent(int duration) { 300 | GradientDrawable drawable = GradientDrawable.class.cast(parent.getBackground()); 301 | int endValue = isFolded ? getResources().getDimensionPixelOffset(R.dimen.morph_radius) : 0; 302 | ObjectAnimator cornerAnimation = ObjectAnimator.ofFloat(drawable, "cornerRadius", endValue); 303 | endValue = isFolded ? parent.getHeight() / 2 : parent.getHeight() * 2; 304 | ValueAnimator heightAnimation = ValueAnimator.ofInt(parent.getHeight(), endValue); 305 | heightAnimation.addUpdateListener(valueAnimator -> { 306 | int val = (Integer) valueAnimator.getAnimatedValue(); 307 | ViewGroup.LayoutParams layoutParams = parent.getLayoutParams(); 308 | layoutParams.height = val; 309 | parent.setLayoutParams(layoutParams); 310 | }); 311 | cornerAnimation.setDuration(duration); 312 | heightAnimation.setDuration(duration); 313 | AnimatorSet set = new AnimatorSet(); 314 | set.playTogether(cornerAnimation, heightAnimation); 315 | return set; 316 | } 317 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/photo/PhotoFragment.kt: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.photo 2 | 3 | import android.animation.AnimatorSet 4 | import android.os.Bundle 5 | import android.support.v4.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.vpaliy.fabexploration.* 10 | import com.vpaliy.kotlin_extensions.getHeightAnimator 11 | import com.vpaliy.kotlin_extensions.info 12 | import com.vpaliy.kotlin_extensions.then 13 | import kotlinx.android.synthetic.main.fragment_photo.view.* 14 | 15 | class PhotoFragment : Fragment() { 16 | 17 | private val size by lazy(LazyThreadSafetyMode.NONE) { 18 | getDimension(R.dimen.photo_size).toInt() 19 | } 20 | 21 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? 22 | = inflater.inflate(R.layout.fragment_photo, container, false) 23 | 24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 25 | super.onViewCreated(view, savedInstanceState) 26 | with(view) { 27 | val startHeight = card.height 28 | val startWidth = card.width 29 | info(this@PhotoFragment, startWidth).info(this@PhotoFragment, startHeight) 30 | card.click { 31 | val height = (card.height != size) then size ?: startHeight 32 | val heightAnimator = card.getHeightAnimator(height) 33 | val width = (card.width != size) then size ?: startWidth 34 | val widthAnimator = card.getWidthAnimator(width) 35 | AnimatorSet().playWith(heightAnimator, widthAnimator).apply { 36 | duration = 300 37 | }.start() 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/player/PlayerFragment.java: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.player; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.animation.ValueAnimator; 8 | import android.graphics.Color; 9 | import android.graphics.Path; 10 | import android.graphics.PathMeasure; 11 | import android.graphics.drawable.Drawable; 12 | import android.os.Bundle; 13 | import android.support.design.widget.FloatingActionButton; 14 | import android.support.v4.content.ContextCompat; 15 | import android.support.v4.view.ViewCompat; 16 | import android.support.v7.widget.Toolbar; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.view.animation.AccelerateDecelerateInterpolator; 20 | import android.view.animation.DecelerateInterpolator; 21 | import android.widget.ImageView; 22 | import android.widget.SeekBar; 23 | import android.widget.TextView; 24 | 25 | import io.codetail.animation.ViewAnimationUtils; 26 | import io.codetail.widget.RevealFrameLayout; 27 | 28 | import com.vpaliy.fabexploration.BaseFragment; 29 | import com.vpaliy.fabexploration.R; 30 | 31 | import java.util.List; 32 | 33 | import butterknife.BindView; 34 | import butterknife.BindViews; 35 | import butterknife.OnClick; 36 | 37 | import android.support.annotation.DrawableRes; 38 | import android.support.annotation.Nullable; 39 | 40 | public class PlayerFragment extends BaseFragment { 41 | 42 | @BindView(R.id.fab) 43 | protected FloatingActionButton actionButton; 44 | 45 | @BindView(R.id.parent) 46 | protected ViewGroup parent; 47 | 48 | @BindView(R.id.background) 49 | protected View background; 50 | 51 | @BindView(R.id.controls_panel) 52 | protected ViewGroup panel; 53 | 54 | @BindViews({R.id.album, R.id.track_author}) 55 | protected List fadeViews; 56 | 57 | @BindView(R.id.seekbar) 58 | protected SeekBar seekBar; 59 | 60 | @BindView(R.id.divider) 61 | protected View divider; 62 | 63 | @BindView(R.id.bottom_background) 64 | protected View bottomBackground; 65 | 66 | @BindView(R.id.track_title) 67 | protected TextView trackTitle; 68 | 69 | @BindView(R.id.sound_play) 70 | protected ImageView soundPlay; 71 | 72 | @BindView(R.id.action_bar) 73 | protected Toolbar actionBar; 74 | 75 | @BindView(R.id.play_pause) 76 | protected ImageView playPause; 77 | 78 | @BindView(R.id.next) 79 | protected ImageView next; 80 | 81 | @BindView(R.id.prev) 82 | protected ImageView prev; 83 | 84 | @BindView(R.id.controls) 85 | protected RevealFrameLayout revealContainer; 86 | 87 | private Animator revealAnimator; 88 | 89 | @Override 90 | protected int mainRes() { 91 | return R.layout.fragment_player; 92 | } 93 | 94 | @Override 95 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 96 | super.onViewCreated(view, savedInstanceState); 97 | if (view != null) { 98 | setUpActionBar(); 99 | setUpPanel(); 100 | setUpButton(); 101 | } 102 | } 103 | 104 | private void setUpButton() { 105 | divider.post(() -> { 106 | float offsetY = divider.getY() - (actionButton.getY() + actionButton.getHeight() / 2); 107 | actionButton.setTranslationY(offsetY); 108 | }); 109 | } 110 | 111 | 112 | private void setUpPanel() { 113 | panel.post(() -> { 114 | panel.setOnClickListener(v -> { 115 | final int w = panel.getWidth(); 116 | final int h = panel.getHeight(); 117 | final int endRadius = (int) Math.hypot(w, h); 118 | final float offsetY = (actionButton.getY() + actionButton.getHeight() / 2) - divider.getTop(); 119 | final int cx = (int) (actionButton.getX() + actionButton.getWidth() / 2); 120 | final int cy = (int) (offsetY); 121 | setUpPlayDrawable(); 122 | revealAnimator = ViewAnimationUtils.createCircularReveal(panel, cx, cy, endRadius, actionButton.getHeight() / 2); 123 | revealAnimator.removeAllListeners(); 124 | revealAnimator.addListener(new AnimatorListenerAdapter() { 125 | @Override 126 | public void onAnimationStart(Animator animation) { 127 | super.onAnimationStart(animation); 128 | ViewCompat.setElevation(actionButton, 0); 129 | fadeInOutViews(1, duration(R.integer.fade_duration)); 130 | actionButton.setVisibility(View.VISIBLE); 131 | actionButton.animate() 132 | .alpha(1) 133 | .setDuration(duration(R.integer.fade_in_duration)) 134 | .setListener(null) 135 | .start(); 136 | } 137 | 138 | @Override 139 | public void onAnimationEnd(Animator animation) { 140 | super.onAnimationEnd(animation); 141 | panel.setVisibility(View.GONE); 142 | backAnimation(); 143 | divider.animate() 144 | .setDuration(duration(R.integer.fade_in_duration)) 145 | .scaleY(1).start(); 146 | bottomBackground.setPivotY(0); 147 | bottomBackground.animate() 148 | .setDuration(duration(R.integer.fade_in_duration)) 149 | .scaleY(0).start(); 150 | runIconScale(0, R.drawable.ic_volume_bottom, 151 | ContextCompat.getColor(getContext(), R.color.color_grey)); 152 | setUpReveal(); 153 | } 154 | }); 155 | revealAnimator.setDuration(duration(R.integer.fade_in_duration)); 156 | revealAnimator.start(); 157 | }); 158 | panel.setVisibility(View.GONE); 159 | }); 160 | } 161 | 162 | private void setUpActionBar() { 163 | actionBar.inflateMenu(R.menu.main); 164 | } 165 | 166 | private void setUpReveal() { 167 | int w = panel.getWidth(); 168 | int h = panel.getHeight(); 169 | final int endRadius = (int) Math.hypot(w, h); 170 | final int cx = (int) (actionButton.getX() + actionButton.getWidth() / 2); 171 | final int cy = (int) (actionButton.getY() + actionButton.getHeight() / 2 - background.getTop()); 172 | 173 | final float deltaX = cx - (playPause.getLeft() + playPause.getWidth() / 2); 174 | final float deltaY = (cy - getResources().getDimension(R.dimen.play_pause_size) / 2) - (playPause.getTop()); 175 | playPause.setTranslationX(deltaX); 176 | playPause.setTranslationY(deltaY); 177 | revealAnimator = ViewAnimationUtils.createCircularReveal(panel, cx, cy, actionButton.getHeight(), endRadius); 178 | revealAnimator.addListener(new AnimatorListenerAdapter() { 179 | @Override 180 | public void onAnimationStart(Animator animation) { 181 | panel.setVisibility(View.VISIBLE); 182 | actionButton.setVisibility(View.INVISIBLE); 183 | fadeInOutViews(0, duration(R.integer.fade_in_duration)); 184 | } 185 | }); 186 | revealAnimator.setDuration(duration(R.integer.conceal_duration) / 2); 187 | revealAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 188 | } 189 | 190 | private void runButtonAnimation() { 191 | next.setScaleX(0); 192 | next.setScaleY(0); 193 | prev.setScaleX(0); 194 | prev.setScaleY(0); 195 | Path arcPath = createArcPath(playPause, 0, 0, -playPause.getTranslationY()); 196 | ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1); 197 | pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 198 | private float point[] = new float[2]; 199 | private PathMeasure pathMeasure = new PathMeasure(arcPath, false); 200 | 201 | @Override 202 | public void onAnimationUpdate(ValueAnimator animation) { 203 | final float value = animation.getAnimatedFraction(); 204 | // Gets the point at the fractional path length 205 | pathMeasure.getPosTan(pathMeasure.getLength() * value, point, null); 206 | 207 | // Sets view location to the above point 208 | playPause.setTranslationX(point[0]); 209 | playPause.setTranslationY(point[1]); 210 | } 211 | }); 212 | pathAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); 213 | pathAnimator.setDuration(duration(R.integer.path_duration) / 2); 214 | pathAnimator.start(); 215 | next.animate() 216 | .setDuration(duration(R.integer.scale_duration)) 217 | .setStartDelay(duration(R.integer.short_delay)) 218 | .scaleX(1).scaleY(1) 219 | .start(); 220 | prev.animate() 221 | .setDuration(duration(R.integer.scale_duration)) 222 | .setStartDelay(duration(R.integer.short_delay)) 223 | .scaleX(1).scaleY(1) 224 | .start(); 225 | } 226 | 227 | private void setUpPauseDrawable() { 228 | Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_pause); 229 | actionButton.setImageDrawable(drawable); 230 | playPause.setImageDrawable(drawable); 231 | } 232 | 233 | private void setUpPlayDrawable() { 234 | Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_play); 235 | actionButton.setImageDrawable(drawable); 236 | playPause.setImageDrawable(drawable); 237 | } 238 | 239 | @OnClick(R.id.fab) 240 | public void onButtonClick() { 241 | setUpPauseDrawable(); 242 | final float playPauseY = playPause.getY() + background.getY(); 243 | float endX = background.getWidth() / 2; 244 | float endY = playPauseY + playPause.getHeight() / 2; 245 | float startX = 0; 246 | float startY = 0; 247 | final float curveRadius = background.getHeight() / 3; 248 | 249 | final float offsetX = endX - (actionButton.getX() + actionButton.getWidth() / 2); 250 | final float offsetY = endY - (actionButton.getY() + actionButton.getHeight() / 2); 251 | 252 | endX = offsetX; 253 | endY = offsetY; 254 | 255 | Path arcPath = new Path(); 256 | 257 | float midX = startX + ((endX - startX) / 2); 258 | float midY = startY + ((endY - startY) / 2); 259 | float xDiff = midX - startX; 260 | float yDiff = midY - startY; 261 | 262 | double angle = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90; 263 | double angleRadians = Math.toRadians(angle); 264 | 265 | float pointX = (float) (midX + curveRadius * Math.cos(angleRadians)); 266 | float pointY = (float) (midY + curveRadius * Math.sin(angleRadians)); 267 | 268 | arcPath.moveTo(0, 0); 269 | arcPath.cubicTo(0, 0, pointX, pointY, endX, endY); 270 | 271 | ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1); 272 | pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 273 | private float point[] = new float[2]; 274 | private volatile boolean isFired; 275 | private PathMeasure pathMeasure = new PathMeasure(arcPath, false); 276 | 277 | @Override 278 | public void onAnimationUpdate(ValueAnimator animation) { 279 | final float value = animation.getAnimatedFraction(); 280 | // Gets the point at the fractional path length 281 | pathMeasure.getPosTan(pathMeasure.getLength() * value, point, null); 282 | 283 | // Sets view location to the above point 284 | actionButton.setTranslationX(point[0]); 285 | actionButton.setTranslationY(point[1]); 286 | 287 | if (!isFired) { 288 | if (animation.getAnimatedFraction() >= 0.35) { 289 | isFired = true; 290 | setUpReveal(); 291 | runButtonAnimation(); 292 | //reveal and animate the thumb 293 | runRevealNProgress(); 294 | //stretch out the top divider 295 | runTopDividerScale(); 296 | //expand the bottom divider 297 | runBottomDividerScale(); 298 | //scale icon, swap icon, scale icon 299 | runIconScale(0, R.drawable.ic_play_bottom, Color.WHITE); 300 | } 301 | } 302 | } 303 | }); 304 | pathAnimator.setInterpolator(new DecelerateInterpolator()); 305 | pathAnimator.setDuration(duration(R.integer.path_duration)); 306 | pathAnimator.start(); 307 | } 308 | 309 | private void runRevealNProgress() { 310 | revealAnimator.setDuration(duration(R.integer.conceal_duration)); 311 | revealAnimator.setInterpolator(new DecelerateInterpolator()); 312 | seekBar.setProgress(80); 313 | ObjectAnimator progressAnimator = ObjectAnimator.ofInt(seekBar, "progress", 80, 20); 314 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(seekBar, View.SCALE_Y, 0, 1f); 315 | progressAnimator.setInterpolator(new DecelerateInterpolator()); 316 | progressAnimator.setDuration(duration(R.integer.progress_duration)); 317 | scaleY.setDuration(duration(R.integer.progress_duration)); 318 | AnimatorSet animatorSet = new AnimatorSet(); 319 | animatorSet.play(revealAnimator); 320 | animatorSet.play(progressAnimator).with(scaleY); 321 | animatorSet.start(); 322 | } 323 | 324 | private void fadeInOutViews(int alpha, int duration) { 325 | for (final View view : fadeViews) { 326 | view.animate() 327 | .alpha(alpha) 328 | .setDuration(duration) 329 | .setListener(new AnimatorListenerAdapter() { 330 | @Override 331 | public void onAnimationEnd(Animator animation) { 332 | super.onAnimationEnd(animation); 333 | view.setVisibility(alpha < 1 ? View.INVISIBLE : View.VISIBLE); 334 | } 335 | }).start(); 336 | } 337 | } 338 | 339 | private void runIconScale(int delay, @DrawableRes int drawable, int color) { 340 | soundPlay.animate() 341 | .scaleY(0) 342 | .scaleX(0) 343 | .setDuration(duration(R.integer.short_delay)) 344 | .setStartDelay(delay) 345 | .setListener(new AnimatorListenerAdapter() { 346 | @Override 347 | public void onAnimationEnd(Animator animation) { 348 | super.onAnimationEnd(animation); 349 | trackTitle.setTextColor(color); 350 | soundPlay.setImageDrawable(ContextCompat.getDrawable(getContext(), drawable)); 351 | soundPlay.animate() 352 | .scaleX(1) 353 | .scaleY(1) 354 | .setDuration(duration(R.integer.scale_duration)) 355 | .setListener(null).start(); 356 | } 357 | }).start(); 358 | } 359 | 360 | private void runBottomDividerScale() { 361 | bottomBackground.setPivotY(0); 362 | bottomBackground.animate() 363 | .setDuration(duration(R.integer.divider_duration)) 364 | .scaleY(100).start(); 365 | } 366 | 367 | private void runTopDividerScale() { 368 | divider.animate() 369 | .setDuration(duration(R.integer.divider_duration)) 370 | .scaleY(30).start(); 371 | } 372 | 373 | 374 | private void backAnimation() { 375 | float endX = 0; 376 | float endY = 0; 377 | float startX = actionButton.getTranslationX(); 378 | float startY = actionButton.getTranslationY(); 379 | final float curveRadius = -background.getHeight() / 2; 380 | 381 | Path arcPath = new Path(); 382 | 383 | float midX = startX + ((endX - startX) / 2); 384 | float midY = startY + ((endY - startY) / 2); 385 | float xDiff = midX - startX; 386 | float yDiff = midY - startY; 387 | 388 | double angle = (Math.atan2(yDiff, xDiff) * (180 / Math.PI)) - 90; 389 | double angleRadians = Math.toRadians(angle); 390 | 391 | float pointX = (float) (midX + curveRadius * Math.cos(angleRadians)); 392 | float pointY = (float) (midY + curveRadius * Math.sin(angleRadians)); 393 | 394 | arcPath.moveTo(startX, startY); 395 | arcPath.cubicTo(startX, startY, pointX, pointY, endX, endY); 396 | 397 | ValueAnimator pathAnimator = ValueAnimator.ofFloat(0, 1); 398 | pathAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 399 | private float point[] = new float[2]; 400 | private PathMeasure pathMeasure = new PathMeasure(arcPath, false); 401 | 402 | @Override 403 | public void onAnimationUpdate(ValueAnimator animation) { 404 | final float value = animation.getAnimatedFraction(); 405 | // Gets the point at the fractional path length 406 | pathMeasure.getPosTan(pathMeasure.getLength() * value, point, null); 407 | 408 | // Sets view location to the above point 409 | actionButton.setTranslationX(point[0]); 410 | actionButton.setTranslationY(point[1]); 411 | } 412 | }); 413 | pathAnimator.setInterpolator(new DecelerateInterpolator()); 414 | pathAnimator.setDuration(duration(R.integer.path_duration)); 415 | pathAnimator.start(); 416 | } 417 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/share/ShareFragment.kt: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.share 2 | 3 | import android.animation.ValueAnimator 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.view.animation.AccelerateInterpolator 7 | import android.view.animation.DecelerateInterpolator 8 | import com.vpaliy.fabexploration.* 9 | import com.vpaliy.kotlin_extensions.animator 10 | import com.vpaliy.kotlin_extensions.hide 11 | import com.vpaliy.kotlin_extensions.scale 12 | import com.vpaliy.kotlin_extensions.show 13 | import io.codetail.animation.ViewAnimationUtils 14 | import kotlinx.android.synthetic.main.fragment_share.* 15 | 16 | class ShareFragment : BaseFragment() { 17 | private val items by lazy(LazyThreadSafetyMode.NONE) { 18 | mutableListOf(git, facebook, linkedIn, twitter) 19 | } 20 | 21 | override fun mainRes() = R.layout.fragment_share 22 | 23 | override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | share.click { 26 | val deltaX = root.halfWidth() - share.x - share.halfWidth() 27 | val deltaY = root.halfHeight() - share.y - share.halfHeight() 28 | val path = createArcPath(share, deltaX, deltaY, -deltaX) 29 | ValueAnimator.ofFloat(0f, 1f).apply { 30 | addUpdateListener(ArcListener(path, share)) 31 | onEnd { reveal() } 32 | }.start() 33 | } 34 | 35 | background.click { conceal() } 36 | } 37 | 38 | private fun reveal() { 39 | val cx = share.x + share.halfWidth() 40 | val cy = share.y + share.halfHeight() 41 | val endRadius = Math.hypot(root.width.toDouble(), root.height.toDouble()).toFloat() 42 | val startRadius = share.height.toFloat() 43 | share.hide(false) 44 | background.show() 45 | ViewAnimationUtils.createCircularReveal(background, cx.toInt(), cy.toInt(), startRadius, endRadius).apply { 46 | duration = 850 47 | interpolator = DecelerateInterpolator() 48 | onStart { 49 | items.forEachIndexed { index, item -> 50 | item.animator { 51 | scale(1f) 52 | duration = 150L 53 | startDelay = maxOf(index, 1) * 90L 54 | }.start() 55 | } 56 | } 57 | }.start() 58 | } 59 | 60 | private fun conceal() { 61 | val cx = share.x + share.halfWidth() 62 | val cy = share.y + share.halfHeight() 63 | val startRadius = Math.hypot(root.width.toDouble(), root.height.toDouble()).toFloat() 64 | val endRadius = share.height.toFloat() 65 | ViewAnimationUtils.createCircularReveal(background, cx.toInt(), cy.toInt(), startRadius, endRadius).onStart { 66 | items.forEachIndexed { index, item -> 67 | item.animator { 68 | scale(0f) 69 | duration = 300L 70 | startDelay = index * 50L 71 | }.start() 72 | } 73 | }.onEnd { 74 | background.hide(false) 75 | share.show() 76 | adjustButton() 77 | }.animator().apply { 78 | duration = 500 79 | interpolator = AccelerateInterpolator() 80 | }.start() 81 | } 82 | 83 | private fun adjustButton() { 84 | val path = createArcPath(share, 0f, 0f, share.translationX) 85 | ValueAnimator.ofFloat(0f, 1f).apply { 86 | startDelay = 300L 87 | addUpdateListener(ArcListener(path, share)) 88 | }.start() 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/sheets/SheetAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.sheets 2 | 3 | import android.content.Context 4 | import android.support.v7.widget.RecyclerView 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import com.vpaliy.fabexploration.* 9 | 10 | class SheetAdapter(context: Context) : RecyclerView.Adapter() { 11 | private val inflater: LayoutInflater = LayoutInflater.from(context) 12 | private val expandedHeight = context.getDimens(R.dimen.sheet_expanded_height) 13 | private val shrunkHeight = context.getDimens(R.dimen.sheet_shrunk_height) 14 | private val heights = IntArray(itemCount, { shrunkHeight }) 15 | 16 | inner class SheetViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 17 | init { 18 | itemView.setElevation(R.dimen.sheet_elevation) 19 | itemView.click { 20 | val height = (heights[adapterPosition] == shrunkHeight) then expandedHeight ?: shrunkHeight 21 | itemView.getHeightAnimator(height).start() 22 | heights[adapterPosition] = height 23 | } 24 | } 25 | 26 | fun bind() = itemView.setHeight(heights[adapterPosition]) 27 | } 28 | 29 | override fun getItemCount() = 10 30 | 31 | override fun onBindViewHolder(holder: SheetViewHolder, position: Int) = holder.bind() 32 | 33 | override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int) 34 | : SheetViewHolder 35 | = SheetViewHolder(inflater.inflate(R.layout.adapter_sheet, parent, false)) 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/vpaliy/fabexploration/sheets/SheetFragment.kt: -------------------------------------------------------------------------------- 1 | package com.vpaliy.fabexploration.sheets 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.support.v7.widget.RecyclerView 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.vpaliy.fabexploration.R 10 | 11 | class SheetFragment : Fragment() { 12 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 13 | val root = inflater.inflate(R.layout.fragment_sheets, container, false) as RecyclerView 14 | root.adapter = SheetAdapter(context) 15 | return root 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/city.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/app/src/main/res/drawable-nodpi/city.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/drawer_icon_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/drawer_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/github.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/app/src/main/res/drawable/header.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_facebook.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fast_forward.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fast_rewind.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_google.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_linkedin.xml: -------------------------------------------------------------------------------- 1 | 6 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_overflow.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pause.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_bottom.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_next.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_prev.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_star.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_twitter.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_bottom.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_high.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_medium.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/id.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/app/src/main/res/drawable/id.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/kodaline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/app/src/main/res/drawable/kodaline.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/morph_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/photo_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sheet_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/adapter_sheet.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_card.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 17 | 22 | 27 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dots.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 17 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_photo.xml: -------------------------------------------------------------------------------- 1 | 8 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_player.xml: -------------------------------------------------------------------------------- 1 | 10 | 22 | 29 | 39 | 48 | 62 | 72 | 85 | 95 | 105 | 116 | 128 | 140 | 153 | 154 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_share.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 35 | 36 | 49 | 50 | 63 | 64 | 78 | 79 | 91 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_sheets.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/header.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_controls.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | 19 | 29 | 30 | 40 | 41 | 53 | 54 | 69 | 70 | 81 | 82 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_dots.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 25 | 26 | 27 | 38 | 39 | 48 | 49 | 59 | 60 | 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_fab.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_photo.xml: -------------------------------------------------------------------------------- 1 | 9 | 17 | 24 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_toolbar.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 21 | 26 | 27 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/menu/drawer_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | 11 | 14 | 17 | 20 | 23 | 24 | 26 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-nodpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/app/src/main/res/mipmap-nodpi/icon.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #f612517b 4 | #009688 5 | #FF4081 6 | 7 | #004e47 8 | #016e64 9 | 10 | #40808080 11 | #fafafa 12 | #fff5f5f5 13 | #99323232 14 | #8a000000 15 | #e57373 16 | 17 | #03a9f4 18 | #ffc107 19 | #c4c4c4 20 | 21 | #fafafa 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 160dp 4 | 28dp 5 | 230dp 6 | 24dp 7 | 8 | 9 | 8dp 10 | 16dp 11 | 24dp 12 | 32dp 13 | 4dp 14 | 42dp 15 | 16 | 17 | 16sp 18 | 18sp 19 | 20 | 21 | 17sp 22 | 13sp 23 | 24sp 24 | 18sp 25 | 26 | 100dp 27 | 10dp 28 | 150dp 29 | 30 | 31 | 300dp 32 | 150dp 33 | 12dp 34 | 35 | 150dp 36 | 64dp 37 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 500 5 | 400 6 | 150 7 | 300 8 | 50 9 | 150 10 | 350 11 | 100 12 | 13 | 14 | 150 15 | 20 16 | 300 17 | 300 18 | 375 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Material Motion 3 | Open the left drawer 4 | This message will disappear in 5 | Warning 6 | Player 7 | Dots 8 | Sheets 9 | PhotoCard 10 | Kodaline 11 | In a Perfect World 12 | GitHub 13 | Share 14 | https://www.github.com/vpaliyX 15 | 16 | GitHub 17 | Twitter 18 | Google 19 | Facebook 20 | LinkedIn 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 17 | 18 | 23 | 24 | 32 | 33 | 38 | 39 | -------------------------------------------------------------------------------- /art/dot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/art/dot.gif -------------------------------------------------------------------------------- /art/dots_g.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/art/dots_g.gif -------------------------------------------------------------------------------- /art/player.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/art/player.gif -------------------------------------------------------------------------------- /art/sheets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/art/sheets.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.1.51' 3 | repositories { 4 | jcenter() 5 | google() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.4' 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | jcenter() 16 | mavenCentral() 17 | maven { url 'https://maven.google.com' } 18 | maven { 19 | url 'https://jitpack.io' 20 | } 21 | google() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 18 08:35:12 CDT 2018 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-4.4-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 | -------------------------------------------------------------------------------- /keys.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vpaliy/android-material-motion/add81283f4471065dfe54c123fc5f25c797da213/keys.jks -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------