├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable │ │ │ ├── metalogo.png │ │ │ ├── empty.xml │ │ │ ├── shape_thumb.xml │ │ │ ├── ic_home_black_24dp.xml │ │ │ ├── ic_pause.xml │ │ │ ├── ic_volume_medium.xml │ │ │ ├── ic_fast_forward.xml │ │ │ ├── ic_fast_rewind.xml │ │ │ ├── ic_baseline_arrow_back_24.xml │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ ├── ic_outline_play_arrow_24.xml │ │ │ ├── ic_arrow_back.xml │ │ │ ├── ic_baseline_reorder_24.xml │ │ │ ├── ic_baseline_save_24.xml │ │ │ ├── ic_volume_high.xml │ │ │ ├── ic_volume_bottom.xml │ │ │ ├── ic_notifications_black_24dp.xml │ │ │ ├── ic_baseline_search_24.xml │ │ │ └── ic_launcher_background.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── layout │ │ │ ├── activity_animations.xml │ │ │ ├── activity_window_animations.xml │ │ │ ├── custom_layout.xml │ │ │ ├── activity_home.xml │ │ │ ├── fragment_home.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_host_activty.xml │ │ │ ├── item_artist.xml │ │ │ ├── fragment_add_note.xml │ │ │ ├── fragment_list.xml │ │ │ ├── fragment_dashboard.xml │ │ │ ├── artist_linear_list.xml │ │ │ └── fragment_notifications.xml │ │ ├── raw │ │ │ ├── grid.json │ │ │ └── list.json │ │ ├── values │ │ │ ├── integers.xml │ │ │ ├── strings.xml │ │ │ ├── dimens.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── anim │ │ │ ├── layout_animation_fall_down.xml │ │ │ └── item_animation_fall_down.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── menu │ │ │ └── bottom_nav_menu.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── navigation │ │ │ └── mobile_navigation.xml │ │ ├── java │ │ └── com │ │ │ └── androidpoet │ │ │ └── metaphordemo │ │ │ ├── ui │ │ │ ├── home │ │ │ │ ├── SampleResponse.kt │ │ │ │ ├── ArtistViewHolder.kt │ │ │ │ ├── ArtistLinearViewHolder.kt │ │ │ │ ├── AddNoteFragment.kt │ │ │ │ ├── ArtistGridListAdapter.kt │ │ │ │ ├── ArtistLinearListAdapter.kt │ │ │ │ ├── ArtistDetailFragment.kt │ │ │ │ └── ArtistListFragment.kt │ │ │ ├── notifications │ │ │ │ └── NotificationsFragment.kt │ │ │ └── dashboard │ │ │ │ └── DashboardFragment.kt │ │ │ ├── factory │ │ │ ├── MetaphorFragmentFactory.kt │ │ │ └── MetaphorActivityFactory.kt │ │ │ ├── activties │ │ │ ├── MainActivity.kt │ │ │ └── FragmentHostActivty.kt │ │ │ └── MetaphorUtils.kt │ │ └── AndroidManifest.xml ├── .editorconfig ├── proguard-rules.pro └── build.gradle ├── spotless.license.kt ├── metaphor ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── androidpoet │ │ │ └── metaphor │ │ │ ├── MetaphorInterpolator.kt │ │ │ ├── MetaphorAnimation.kt │ │ │ ├── WindowExtenstions.kt │ │ │ ├── MetaphorLazyExtension.kt │ │ │ ├── internals │ │ │ ├── FragmentMetaphorLazy.kt │ │ │ └── ActivityMetaphorLazy.kt │ │ │ ├── FragmentExtensions.kt │ │ │ ├── MetaphorView.kt │ │ │ ├── ActivityExtenstions.kt │ │ │ ├── MetaphorWindow.kt │ │ │ ├── ViewExtensions.kt │ │ │ ├── AnimationsExtenstions.kt │ │ │ ├── MetaphorActivity.kt │ │ │ └── MetaphorFragment.kt │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── metaphormotion.xml │ │ │ └── ids.xml │ │ └── drawable │ │ │ ├── circle_gray.xml │ │ │ ├── circle_white.xml │ │ │ ├── ic_baseline_add_24.xml │ │ │ ├── ic_home_gray_24dp.xml │ │ │ └── ic_home_white_24dp.xml │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github └── FUNDING.yml ├── .gitignore ├── usecases.md ├── settings.gradle ├── spotless.gradle ├── dependencies.gradle ├── gradlew.bat ├── gradle.properties └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /spotless.license.kt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /metaphor/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /metaphor/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/metalogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/drawable/metalogo.png -------------------------------------------------------------------------------- /app/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | # Most of the standard properties are supported 4 | indent_size=2 5 | max_line_length=100 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: AndroidPoet 3 | custom: ["https://www.buymeacoffee.com/AndroidPoet"] 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndroidPoet/Metaphor/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorInterpolator.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | public enum class MetaphorInterpolator(public val value: Int) { 5 | Standard(2), 6 | Emphasized(3), 7 | Decelerated(4), 8 | Accelerated(5), 9 | Linear(6) 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/SampleResponse.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.os.Parcelable 5 | import kotlinx.parcelize.Parcelize 6 | 7 | @Parcelize 8 | 9 | data class SampleResponse(var pos: Int, val blur: String, val img: String) : Parcelable 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistViewHolder.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.androidpoet.metaphordemo.databinding.ItemArtistBinding 6 | 7 | class ArtistViewHolder(val binding: ItemArtistBinding) : RecyclerView.ViewHolder(binding.root) 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistLinearViewHolder.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.androidpoet.metaphordemo.databinding.ArtistLinearListBinding 6 | 7 | class ArtistLinearViewHolder(val binding: ArtistLinearListBinding) : RecyclerView.ViewHolder(binding.root) 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_animations.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_window_animations.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/factory/MetaphorFragmentFactory.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.factory 3 | 4 | import androidx.fragment.app.Fragment 5 | import com.androidpoet.metaphor.MetaphorAnimation 6 | import com.androidpoet.metaphor.MetaphorFragment 7 | 8 | class MetaphorFragmentFactory : MetaphorFragment.Factory() { 9 | override fun create(fragment: Fragment): MetaphorFragment { 10 | return MetaphorFragment.Builder(fragment) 11 | .setEnterAnimation(MetaphorAnimation.ElevationScale) 12 | .setExitAnimation(MetaphorAnimation.SharedAxisXBackward) 13 | .build() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/factory/MetaphorActivityFactory.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.factory 3 | 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.androidpoet.metaphor.MetaphorActivity 6 | import com.androidpoet.metaphor.MetaphorAnimation 7 | 8 | class MetaphorActivityFactory() : MetaphorActivity.Factory() { 9 | override fun create(fragment: AppCompatActivity): MetaphorActivity { 10 | return MetaphorActivity.Builder(fragment) 11 | .setEnterAnimation(MetaphorAnimation.ElevationScale) 12 | .setExitAnimation(MetaphorAnimation.SharedAxisXBackward) 13 | .build() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /usecases.md: -------------------------------------------------------------------------------- 1 | 2 | # Who's using Metaphor? 3 | 4 | If your project uses Metaphor, let me know by creating a new issue or PR! 🤗 5 | 6 | 7 | 8 | # License 9 | ```xml 10 | Copyright 2022 AndroidPoet (Ranbir Singh) 11 | 12 | Licensed under the Apache License, Version 2.0 (the "License"); 13 | you may not use this file except in compliance with the License. 14 | You may obtain a copy of the License at 15 | 16 | http://www.apache.org/licenses/LICENSE-2.0 17 | 18 | Unless required by applicable law or agreed to in writing, software 19 | distributed under the License is distributed on an "AS IS" BASIS, 20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | See the License for the specific language governing permissions and 22 | limitations under the License. 23 | ``` 24 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 5 | * * 6 | * * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * * you may not use this file except in compliance with the License. 8 | * * You may obtain a copy of the License at 9 | * * 10 | * * http://www.apache.org/licenses/LICENSE-2.0 11 | * * 12 | * * Unless required by applicable law or agreed to in writing, software 13 | * * distributed under the License is distributed on an "AS IS" BASIS, 14 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * * See the License for the specific language governing permissions and 16 | * * limitations under the License. 17 | * 18 | * 19 | */ 20 | 21 | rootProject.name = "MetaphorDemo" 22 | include ':app' 23 | include ':metaphor' 24 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /metaphor/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /metaphor/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/raw/grid.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blur": "LEHV6nWB2yk8pyoJadR*.7kCMdnj", 4 | "pos": 1, 5 | "img": "https://unsplash.com/photos/MvxMvPO3S1M/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTA0fHx3b21hbnxlbnwwfHx8fDE2NDY2NTI2ODE&force=true&w=640" 6 | }, 7 | { 8 | "blur": "LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 9 | "pos": 2, 10 | "img": "https://unsplash.com/photos/kg9YQ1aDQr8/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8NnNNVmpUTFNrZVF8fHx8fDJ8fDE2NDY2NTI3MjA&force=true&w=640" 11 | }, 12 | { 13 | "blur": "L6Pj0^i_.AyE_3t7t7R**0o#DgR4", 14 | "pos": 3, 15 | "img": "https://unsplash.com/photos/2fa1zztCgBQ/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8NnNNVmpUTFNrZVF8fHx8fDJ8fDE2NDY2NTI3MjA&force=true&w=640" 16 | }, 17 | { 18 | "blur": "LKO2?U%2Tw=w]~RBVZRi};RPxuwH", 19 | "pos": 4, 20 | "img": "https://unsplash.com/photos/aNo_entwaVY/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8X2hiLWRsNFEtNFV8fHx8fDJ8fDE2NDY2NTI3Njg&force=true&w=640" 21 | } 22 | ] -------------------------------------------------------------------------------- /app/src/main/res/raw/list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "blur": "LEHV6nWB2yk8pyoJadR*.7kCMdnj", 4 | "pos": 1, 5 | "img": "https://unsplash.com/photos/cuPH-o6TmvI/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8cm5TS0RId3dZVWt8fHx8fDJ8fDE2NDY2NTI3OTQ&force=true&w=640" 6 | }, 7 | { 8 | "blur": "LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 9 | "pos": 2, 10 | "img": "https://unsplash.com/photos/SQeoekKJ--4/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8Ym84alFLVGFFMFl8fHx8fDJ8fDE2NDY2NTI4MTQ&force=true&w=640" 11 | }, 12 | { 13 | "blur": "L6Pj0^i_.AyE_3t7t7R**0o#DgR4", 14 | "pos": 3, 15 | "img": "https://unsplash.com/photos/W1PaOgpZ-J0/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8d256cEx4czBuUVl8fHx8fDJ8fDE2NDY2NTI4NDU&force=true&w=640" 16 | }, 17 | { 18 | "blur": "LKO2?U%2Tw=w]~RBVZRi};RPxuwH", 19 | "pos": 4, 20 | "img": "https://unsplash.com/photos/5SXBhUVR9_Y/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8eGpQUjRobGtCR0F8fHx8fDJ8fDE2NDY2NTI4NzU&force=true&w=640" 21 | } 22 | ] -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 300 22 | 400 23 | -------------------------------------------------------------------------------- /metaphor/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 26 | 27 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 4 | # /* 5 | # * Copyright 2022 AndroidPoet (Ranbir Singh) 6 | # * 7 | # * Licensed under the Apache License, Version 2.0 (the "License"); 8 | # * you may not use this file except in compliance with the License. 9 | # * You may obtain a copy of the License at 10 | # * 11 | # * http://www.apache.org/licenses/LICENSE-2.0 12 | # * 13 | # * Unless required by applicable law or agreed to in writing, software 14 | # * distributed under the License is distributed on an "AS IS" BASIS, 15 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # * See the License for the specific language governing permissions and 17 | # * limitations under the License. 18 | # */ 19 | # 20 | # 21 | 22 | #Wed Mar 02 13:27:46 IST 2022 23 | distributionBase=GRADLE_USER_HOME 24 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 25 | distributionPath=wrapper/dists 26 | zipStorePath=wrapper/dists 27 | zipStoreBase=GRADLE_USER_HOME 28 | -------------------------------------------------------------------------------- /app/src/main/res/anim/layout_animation_fall_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/empty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/custom_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 27 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /metaphor/src/main/res/drawable/circle_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 26 | 27 | 30 | 31 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /spotless.gradle: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | * 5 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 6 | * * 7 | * * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * * you may not use this file except in compliance with the License. 9 | * * You may obtain a copy of the License at 10 | * * 11 | * * http://www.apache.org/licenses/LICENSE-2.0 12 | * * 13 | * * Unless required by applicable law or agreed to in writing, software 14 | * * distributed under the License is distributed on an "AS IS" BASIS, 15 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * * See the License for the specific language governing permissions and 17 | * * limitations under the License. 18 | * 19 | * 20 | */ 21 | 22 | apply plugin: "com.diffplug.spotless" 23 | apply from: "$rootDir/dependencies.gradle" 24 | spotless { 25 | kotlin { 26 | target "**/*.kt" 27 | ktlint("$versions.ktlintGradle").userData(['indent_size': '2', 'continuation_indent_size': '2']) 28 | licenseHeaderFile "$rootDir/spotless.license.kt" 29 | trimTrailingWhitespace() 30 | endWithNewline() 31 | } 32 | } -------------------------------------------------------------------------------- /metaphor/src/main/res/values/metaphormotion.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 24 | 25 | 300 26 | 225 27 | 175 28 | 29 | -------------------------------------------------------------------------------- /metaphor/src/main/res/drawable/circle_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 26 | 27 | 30 | 31 | 33 | 34 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /metaphor/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /metaphor/src/main/res/drawable/ic_home_gray_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pause.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_medium.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fast_forward.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /metaphor/src/main/res/drawable/ic_home_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 25 | 26 | 31 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_fast_rewind.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | MetaphorDemo 22 | 23 | Home 24 | Dashboard 25 | Notifications 26 | Artist 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 25 | 26 | 160dp 27 | 28dp 28 | 230dp 29 | 30 | 31 | 32dp 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_reorder_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_save_24.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/activties/MainActivity.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.activties 3 | 4 | import android.content.Intent 5 | import android.os.Build 6 | import android.os.Bundle 7 | import android.os.Handler 8 | import android.view.View 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.androidpoet.metaphordemo.R 11 | import com.androidpoet.metaphordemo.databinding.ActivityMainBinding 12 | import com.bumptech.glide.Glide 13 | 14 | class MainActivity : AppCompatActivity() { 15 | 16 | private lateinit var binding: ActivityMainBinding 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | binding = ActivityMainBinding.inflate(layoutInflater) 21 | setContentView(binding.root) 22 | 23 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 24 | val decor: View = window.decorView 25 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) 26 | } 27 | 28 | Glide.with(this).load(R.drawable.metalogo).into(binding.logo) 29 | Handler().postDelayed( 30 | { 31 | val intent = Intent(this, FragmentHostActivty::class.java) 32 | startActivity(intent) 33 | finish() 34 | }, 35 | 2000 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_high.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_volume_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorAnimation.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | /* 5 | * 6 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 7 | * * 8 | * * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * * you may not use this file except in compliance with the License. 10 | * * You may obtain a copy of the License at 11 | * * 12 | * * http://www.apache.org/licenses/LICENSE-2.0 13 | * * 14 | * * Unless required by applicable law or agreed to in writing, software 15 | * * distributed under the License is distributed on an "AS IS" BASIS, 16 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * * See the License for the specific language governing permissions and 18 | * * limitations under the License. 19 | * 20 | * 21 | */ 22 | 23 | /** MetaphorAnimation is an animation attribute of [MetaphorAnimation]'s the showing and dismissing. */ 24 | 25 | public enum class MetaphorAnimation(public val value: Int) { 26 | None(1), 27 | ContainerTransform(2), 28 | FadeThrough(3), 29 | Fade(4), 30 | SharedAxisXForward(5), 31 | SharedAxisYForward(6), 32 | SharedAxisZForward(7), 33 | SharedAxisXBackward(8), 34 | SharedAxisYBackward(9), 35 | SharedAxisZBackward(10), 36 | ElevationScaleGrow(11), 37 | ElevationScale(12), 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 20 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/WindowExtenstions.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.os.Build 5 | import android.widget.PopupWindow 6 | import androidx.annotation.RequiresApi 7 | 8 | /** applies Animation form attributes to a View instance. */ 9 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 10 | @JvmSynthetic 11 | internal fun PopupWindow.applyAnimation( 12 | window: MetaphorWindow 13 | ) { 14 | 15 | val enterAnimation = 16 | getWindowMetaphorAnimation(window.enterAnimation)?.let { 17 | addAnimationProperties( 18 | it, 19 | window, 20 | window.enterDuration 21 | ) 22 | } 23 | val exitAnimation = 24 | getWindowMetaphorAnimation(window.exitAnimation)?.let { 25 | addAnimationProperties( 26 | it, 27 | window, 28 | window.exitDuration 29 | ) 30 | } 31 | 32 | this.enterTransition = enterAnimation 33 | this.exitTransition = exitAnimation 34 | } 35 | 36 | /** applies Properties on Animation form attributes. */ 37 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 38 | @JvmSynthetic 39 | internal fun PopupWindow.addAnimationProperties( 40 | transition: android.transition.Transition, 41 | window: MetaphorWindow, 42 | animationDuration: Long 43 | ): android.transition.Transition { 44 | 45 | transition.apply { 46 | duration = animationDuration 47 | } 48 | return transition 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 30 | 33 | 34 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorLazyExtension.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import androidx.annotation.MainThread 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.fragment.app.Fragment 7 | import com.androidpoet.metaphor.internals.ActivityMetaphorLazy 8 | import com.androidpoet.metaphor.internals.FragmentMetaphorLazy 9 | 10 | /** 11 | * Returns a [Lazy] delegate to access the [MetaphorFragment]'s Balloon property. 12 | * The balloon property will be initialized lazily. 13 | * 14 | * @see [Lazy Initialization](https://github.com/skydoves/Balloon#lazy-initialization) 15 | */ 16 | @MainThread 17 | @JvmSynthetic 18 | @MetaphorFragmentInlineDsl 19 | public inline fun Fragment.metaphorFragment(): Lazy { 20 | return FragmentMetaphorLazy(fragment = this, factory = T::class) 21 | } 22 | 23 | /** 24 | * Returns a [Lazy] delegate to access the [MetaphorActivity]'s Balloon property. 25 | * The balloon property will be initialized lazily. 26 | * 27 | * @see [Lazy Initialization](https://github.com/skydoves/Balloon#lazy-initialization) 28 | */ 29 | @MainThread 30 | @JvmSynthetic 31 | @MetaphorActivityInlineDsl 32 | public inline fun AppCompatActivity.metaphorActivity(): Lazy { 33 | return ActivityMetaphorLazy(activity = this, factory = T::class) 34 | } 35 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/internals/FragmentMetaphorLazy.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor.internals 3 | 4 | import androidx.fragment.app.Fragment 5 | import com.androidpoet.metaphor.MetaphorFragment 6 | import java.io.Serializable 7 | import kotlin.reflect.KClass 8 | 9 | /** 10 | * An implementation of [Lazy] for creating an instance of the [MetaphorFragment] lazily in Fragments. 11 | * 12 | * @param Fragment A context for creating resources of the [MetaphorFragment] lazily. 13 | * @param factory A [MetaphorFragment.Factory] kotlin class for creating a new instance of the MetaphorFragment. 14 | */ 15 | @PublishedApi 16 | internal class FragmentMetaphorLazy( 17 | private val fragment: Fragment, 18 | private val factory: KClass 19 | ) : Lazy, Serializable { 20 | 21 | private var cached: MetaphorFragment? = null 22 | 23 | override val value: MetaphorFragment 24 | get() { 25 | var instance = cached 26 | if (instance === null) { 27 | val factory = factory::java.get().newInstance() 28 | instance = factory.create(fragment) 29 | cached = instance 30 | } 31 | 32 | return instance 33 | } 34 | 35 | override fun isInitialized(): Boolean = cached !== null 36 | 37 | override fun toString(): String = 38 | if (isInitialized()) value.toString() else "Lazy value not initialized yet." 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/internals/ActivityMetaphorLazy.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor.internals 3 | 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.androidpoet.metaphor.MetaphorActivity 6 | import java.io.Serializable 7 | import kotlin.reflect.KClass 8 | 9 | /** 10 | * An implementation of [Lazy] for creating an instance of the [MetaphorActivity] lazily in Activities. 11 | * Tied to the given [lifecycleOwner], [factory]. 12 | * 13 | * @param AppCompatActivity A context for creating resources of the [MetaphorActivity] lazily. 14 | * @param factory A [MetaphorActivity.Factory] kotlin class for creating a new instance of the MetaphorActivity. 15 | */ 16 | @PublishedApi 17 | internal class ActivityMetaphorLazy( 18 | private val activity: AppCompatActivity, 19 | private val factory: KClass 20 | ) : Lazy, Serializable { 21 | 22 | private var cached: MetaphorActivity? = null 23 | 24 | override val value: MetaphorActivity 25 | get() { 26 | var instance = cached 27 | if (instance === null) { 28 | val factory = factory::java.get().newInstance() 29 | instance = factory.create(activity) 30 | cached = instance 31 | } 32 | 33 | return instance 34 | } 35 | 36 | override fun isInitialized(): Boolean = cached !== null 37 | 38 | override fun toString(): String = 39 | if (isInitialized()) value.toString() else "Lazy value not initialized yet." 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/anim/item_animation_fall_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 23 | 24 | 29 | 30 | 35 | 36 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/AddNoteFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.fragment.app.Fragment 11 | import com.androidpoet.metaphordemo.R 12 | import com.androidpoet.metaphordemo.databinding.FragmentAddNoteBinding 13 | import com.google.gson.Gson 14 | import com.google.gson.reflect.TypeToken 15 | 16 | class AddNoteFragment : Fragment() { 17 | 18 | private lateinit var viewBinding: FragmentAddNoteBinding 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | } 23 | 24 | private fun sampleResponse(): List { 25 | val response = resources.openRawResource(R.raw.grid).bufferedReader() 26 | .use { it.readText() } 27 | return Gson().fromJson(response, object : TypeToken>() {}.type) 28 | } 29 | 30 | override fun onCreateView( 31 | inflater: LayoutInflater, 32 | container: ViewGroup?, 33 | savedInstanceState: Bundle? 34 | ): View { 35 | viewBinding = FragmentAddNoteBinding.inflate(inflater, container, false).apply { 36 | lifecycleOwner = viewLifecycleOwner 37 | } 38 | return viewBinding.root 39 | } 40 | 41 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 43 | super.onViewCreated(view, savedInstanceState) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 26 | #FFBB86FC 27 | #FF6200EE 28 | #FF3700B3 29 | #FF03DAC5 30 | #FF018786 31 | #FF000000 32 | #FFFFFFFF 33 | #AFAFAF 34 | #004e47 35 | #016e64 36 | #f612517b 37 | #009688 38 | 39 | 40 | #03a9f4 41 | #2196F3 42 | #F8E21E 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 22 | 23 | 33 | 34 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /dependencies.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 5 | * * 6 | * * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * * you may not use this file except in compliance with the License. 8 | * * You may obtain a copy of the License at 9 | * * 10 | * * http://www.apache.org/licenses/LICENSE-2.0 11 | * * 12 | * * Unless required by applicable law or agreed to in writing, software 13 | * * distributed under the License is distributed on an "AS IS" BASIS, 14 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * * See the License for the specific language governing permissions and 16 | * * limitations under the License. 17 | * 18 | * 19 | */ 20 | 21 | ext.versions = [ 22 | minSdk : 17, 23 | compileSdk : 31, 24 | versionCode : 42, 25 | versionName : '1.4.1', 26 | ktx : '1.7.0', 27 | 28 | gradleBuildTool : '7.1.0', 29 | spotlessGradle : '6.2.1', 30 | kotlin : '1.5.32', 31 | ktlintGradle : '0.43.2', 32 | dokkaGradle : '1.5.31', 33 | binaryValidator : '0.8.0', 34 | mavenPublish : '0.18.0', 35 | 36 | androidxAppcompat: '1.4.1', 37 | androidxFragment : '1.3.3', 38 | lifecycle : '2.4.1', 39 | annotation : '1.3.0', 40 | lottieVersion : '5.0.2', 41 | // for demo 42 | constraintVersion: '2.0.4', 43 | googleMaterial : '1.5.0', 44 | 45 | glide : '4.13.0', 46 | gson : '2.8.6', 47 | recyclerview : '1.2.1' 48 | 49 | 50 | ] 51 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 25 | 26 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 25 | 26 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 29 | 30 | 43 | -------------------------------------------------------------------------------- /metaphor/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/MetaphorUtils.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo 3 | 4 | import android.view.View 5 | import android.widget.PopupWindow 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.fragment.app.Fragment 8 | import com.androidpoet.metaphor.MetaphorActivity 9 | import com.androidpoet.metaphor.MetaphorAnimation 10 | import com.androidpoet.metaphor.MetaphorFragment 11 | import com.androidpoet.metaphor.MetaphorView 12 | import com.androidpoet.metaphor.MetaphorWindow 13 | import com.google.android.material.transition.MaterialArcMotion 14 | 15 | object MetaphorUtils { 16 | 17 | fun getFragmentMetaphor( 18 | fragment: Fragment, 19 | enterAnimation: MetaphorAnimation, 20 | exitAnimation: MetaphorAnimation 21 | ): MetaphorFragment { 22 | 23 | return MetaphorFragment.Builder(fragment) 24 | .setEnterDuration(500) 25 | .setEnterAnimation(enterAnimation) 26 | .setExitAnimation(exitAnimation) 27 | .build() 28 | } 29 | 30 | fun getActivityMetaphor( 31 | appCompatActivity: AppCompatActivity, 32 | enterAnimation: MetaphorAnimation, 33 | exitAnimation: MetaphorAnimation 34 | ): MetaphorActivity { 35 | 36 | return MetaphorActivity.Builder(appCompatActivity) 37 | .setEnterAnimation(enterAnimation) 38 | .setExitAnimation(exitAnimation) 39 | .build() 40 | } 41 | 42 | fun View.getViewMetaphor( 43 | metaphorAnimation: MetaphorAnimation, 44 | end: View 45 | ): MetaphorView { 46 | 47 | return MetaphorView.Builder(this) 48 | .setDuration(1000) 49 | .setEndView(end) 50 | .setMetaphorAnimation(metaphorAnimation) 51 | .setMotion(MaterialArcMotion()) 52 | .build() 53 | } 54 | 55 | fun PopupWindow.getPopupWindowMetaphor( 56 | metaphorAnimation: MetaphorAnimation, 57 | ): MetaphorWindow { 58 | 59 | return MetaphorWindow.Builder(this) 60 | .setEnterDuration(500) 61 | .setExitDuration(500) 62 | .setEnterAnimation(metaphorAnimation) 63 | .setExitAnimation(metaphorAnimation) 64 | .build() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 24 | 29 | 30 | 36 | 37 | 44 | 45 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistGridListAdapter.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.content.Context 5 | import android.os.Build 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.recyclerview.widget.DiffUtil 11 | import androidx.recyclerview.widget.ListAdapter 12 | import com.androidpoet.metaphordemo.databinding.ItemArtistBinding 13 | import com.bumptech.glide.Glide 14 | import com.bumptech.glide.RequestManager 15 | 16 | class ArtistGridListAdapter( 17 | private val context: Context, 18 | private val requestManager: RequestManager 19 | ) : ListAdapter(object : DiffUtil 20 | .ItemCallback() { 21 | override fun areItemsTheSame(oldItem: SampleResponse, newItem: SampleResponse): Boolean { 22 | return oldItem.blur == newItem.blur 23 | } 24 | 25 | override fun areContentsTheSame(oldItem: SampleResponse, newItem: SampleResponse): Boolean { 26 | return oldItem == newItem 27 | } 28 | }) { 29 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArtistViewHolder { 30 | return ArtistViewHolder( 31 | ItemArtistBinding.inflate( 32 | LayoutInflater.from(parent.context), 33 | parent, false 34 | ) 35 | ).apply { 36 | binding.root.setOnClickListener { 37 | if (bindingAdapterPosition >= 0) { 38 | callback?.onClick( 39 | binding.imgv, 40 | getItem(bindingAdapterPosition), 41 | getItem(bindingAdapterPosition).img 42 | ) 43 | } 44 | } 45 | } 46 | } 47 | 48 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 49 | override fun onBindViewHolder(holder: ArtistViewHolder, position: Int) { 50 | Glide.with(holder.itemView.context).load(getItem(position).img).into(holder.binding.imgv) 51 | holder.binding.imgv.transitionName = getItem(position).img 52 | holder.binding.executePendingBindings() 53 | } 54 | 55 | fun setData(list: List) { 56 | submitList(list) 57 | } 58 | 59 | var callback: Callback? = null 60 | 61 | interface Callback { 62 | fun onClick(view: View, item: SampleResponse, imageUrl: String) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistLinearListAdapter.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.content.Context 5 | import android.os.Build 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.recyclerview.widget.DiffUtil 11 | import androidx.recyclerview.widget.ListAdapter 12 | import com.androidpoet.metaphordemo.databinding.ArtistLinearListBinding 13 | import com.bumptech.glide.Glide 14 | import com.bumptech.glide.RequestManager 15 | 16 | /** 17 | * Created by Phong Huynh on 11/15/2020 18 | */ 19 | class ArtistLinearListAdapter( 20 | private val context: Context, 21 | private val requestManager: RequestManager 22 | ) : ListAdapter(object : DiffUtil 23 | .ItemCallback() { 24 | override fun areItemsTheSame(oldItem: SampleResponse, newItem: SampleResponse): Boolean { 25 | return oldItem.blur == newItem.blur 26 | } 27 | 28 | override fun areContentsTheSame(oldItem: SampleResponse, newItem: SampleResponse): Boolean { 29 | return oldItem == newItem 30 | } 31 | }) { 32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArtistLinearViewHolder { 33 | return ArtistLinearViewHolder( 34 | ArtistLinearListBinding.inflate( 35 | LayoutInflater.from(parent.context), 36 | parent, false 37 | ) 38 | ).apply { 39 | binding.root.setOnClickListener { 40 | if (bindingAdapterPosition >= 0) { 41 | callback?.onClick( 42 | binding.imgv, 43 | getItem(bindingAdapterPosition), 44 | getItem(bindingAdapterPosition).img 45 | ) 46 | } 47 | } 48 | } 49 | } 50 | 51 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 52 | override fun onBindViewHolder(holder: ArtistLinearViewHolder, position: Int) { 53 | Glide.with(holder.itemView.context).load(getItem(position).img).into(holder.binding.imgv) 54 | holder.binding.imgv.transitionName = getItem(position).img 55 | holder.binding.executePendingBindings() 56 | } 57 | 58 | fun setData(list: List) { 59 | submitList(list) 60 | } 61 | 62 | var callback: Callback? = null 63 | 64 | interface Callback { 65 | fun onClick(view: View, item: SampleResponse, imageUrl: String) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/FragmentExtensions.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.app.Activity 5 | import android.content.Context 6 | import android.content.ContextWrapper 7 | import android.os.Build 8 | import androidx.annotation.RequiresApi 9 | import androidx.core.view.doOnPreDraw 10 | import androidx.fragment.app.Fragment 11 | import androidx.transition.Transition 12 | import com.google.android.material.transition.MaterialContainerTransform 13 | 14 | public fun Context.activity(): Activity? = when (this) { 15 | is Activity -> this 16 | else -> (this as? ContextWrapper)?.baseContext?.activity() 17 | } 18 | 19 | /** applies Animation form attributes to a View instance. */ 20 | @JvmSynthetic 21 | internal fun Fragment.applyAnimation( 22 | metaphor: MetaphorFragment 23 | ) { 24 | enterTransition = getMetaphorAnimation(metaphor.enterAnimation)?.let { addAnimationProperties(it, metaphor, metaphor.enterDuration) } 25 | exitTransition = getMetaphorAnimation(metaphor.exitAnimation)?.let { addAnimationProperties(it, metaphor, metaphor.exitDuration) } 26 | reenterTransition = getMetaphorAnimation(metaphor.reenterAnimation)?.let { addAnimationProperties(it, metaphor, metaphor.reenterDuration) } 27 | returnTransition = getMetaphorAnimation(metaphor.returnAnimation)?.let { addAnimationProperties(it, metaphor, metaphor.returnDuration) } 28 | } 29 | /** applies Properties on Animation form attributes. */ 30 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 31 | @JvmSynthetic 32 | internal fun Fragment.addAnimationProperties( 33 | transition: Transition, 34 | metaphor: MetaphorFragment, 35 | animationDuration: Long 36 | ): Transition { 37 | 38 | if (transition is MaterialContainerTransform) { 39 | metaphor.view?.transitionName = metaphor.transitionName 40 | sharedElementEnterTransition = transition.apply { 41 | startView?.let { addTarget(it) } 42 | setAllContainerColors(metaphor.containerColors) 43 | scrimColor = metaphor.scrimColor 44 | } 45 | } 46 | 47 | transition.apply { 48 | duration = animationDuration 49 | setPathMotion(metaphor.motion) 50 | allowEnterTransitionOverlap = metaphor.enterTransitionOverlap 51 | allowReturnTransitionOverlap = metaphor.returnTransitionOverlap 52 | } 53 | return transition 54 | } 55 | 56 | // hold extension to use in container transform. 57 | @JvmSynthetic 58 | public fun Fragment.hold() { 59 | postponeEnterTransition() 60 | view?.doOnPreDraw { startPostponedEnterTransition() } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_host_activty.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 28 | 29 | 32 | 33 | 44 | 45 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 23 | 24 | 30 | 31 | 32 | 38 | 41 | 44 | 45 | 46 | 47 | 53 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * 4 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 5 | * * 6 | * * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * * you may not use this file except in compliance with the License. 8 | * * You may obtain a copy of the License at 9 | * * 10 | * * http://www.apache.org/licenses/LICENSE-2.0 11 | * * 12 | * * Unless required by applicable law or agreed to in writing, software 13 | * * distributed under the License is distributed on an "AS IS" BASIS, 14 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * * See the License for the specific language governing permissions and 16 | * * limitations under the License. 17 | * 18 | * 19 | */ 20 | 21 | apply plugin: 'com.android.application' 22 | apply plugin: 'org.jetbrains.kotlin.android' 23 | apply plugin: 'kotlin-kapt' 24 | apply plugin: 'androidx.navigation.safeargs.kotlin' 25 | apply plugin: 'kotlin-parcelize' 26 | apply from: "$rootDir/dependencies.gradle" 27 | 28 | android { 29 | compileSdkVersion versions.compileSdk 30 | defaultConfig { 31 | applicationId "com.androidpoet.metaphordemo" 32 | minSdkVersion versions.minSdk 33 | targetSdkVersion versions.compileSdk 34 | vectorDrawables.useSupportLibrary = true 35 | versionCode versions.versionCode 36 | versionName versions.versionName 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_11 40 | targetCompatibility JavaVersion.VERSION_11 41 | } 42 | kotlinOptions { 43 | jvmTarget = "11" 44 | } 45 | buildFeatures { 46 | viewBinding true 47 | dataBinding true 48 | } 49 | lintOptions { 50 | abortOnError false 51 | } 52 | } 53 | 54 | dependencies { 55 | implementation "androidx.core:core-ktx:$versions.ktx" 56 | implementation "androidx.appcompat:appcompat:$versions.androidxAppcompat" 57 | implementation "com.google.android.material:material:$versions.googleMaterial" 58 | implementation project(":metaphor") 59 | implementation "com.github.bumptech.glide:glide:$versions.glide" 60 | implementation "com.airbnb.android:lottie:$versions.lottieVersion" 61 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$versions.lifecycle" 62 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.lifecycle" 63 | implementation "androidx.navigation:navigation-fragment-ktx:$versions.lifecycle" 64 | implementation "androidx.navigation:navigation-ui-ktx:$versions.lifecycle" 65 | implementation "com.google.code.gson:gson:$versions.gson" 66 | implementation "androidx.recyclerview:recyclerview:$versions.recyclerview" 67 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 68 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 69 | } 70 | 71 | apply from: "$rootDir/spotless.gradle" 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /metaphor/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | * 5 | * * Copyright 2022 AndroidPoet (Ranbir Singh) 6 | * * 7 | * * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * * you may not use this file except in compliance with the License. 9 | * * You may obtain a copy of the License at 10 | * * 11 | * * http://www.apache.org/licenses/LICENSE-2.0 12 | * * 13 | * * Unless required by applicable law or agreed to in writing, software 14 | * * distributed under the License is distributed on an "AS IS" BASIS, 15 | * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * * See the License for the specific language governing permissions and 17 | * * limitations under the License. 18 | * 19 | * 20 | */ 21 | 22 | apply plugin: 'com.android.library' 23 | apply plugin: 'kotlin-android' 24 | apply plugin: 'org.jetbrains.dokka' 25 | apply plugin: 'binary-compatibility-validator' 26 | apply from: "$rootDir/dependencies.gradle" 27 | 28 | android { 29 | compileSdkVersion versions.compileSdk 30 | defaultConfig { 31 | minSdkVersion versions.minSdk 32 | targetSdkVersion versions.compileSdk 33 | versionCode versions.versionCode 34 | versionName versions.versionName 35 | } 36 | 37 | resourcePrefix 'metaphor' 38 | 39 | buildFeatures { 40 | buildConfig false 41 | viewBinding true 42 | } 43 | 44 | compileOptions { 45 | sourceCompatibility JavaVersion.VERSION_1_8 46 | targetCompatibility JavaVersion.VERSION_1_8 47 | } 48 | 49 | kotlinOptions { 50 | jvmTarget = "1.8" 51 | } 52 | 53 | lintOptions { 54 | abortOnError false 55 | } 56 | } 57 | 58 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { 59 | kotlinOptions.freeCompilerArgs += ["-Xexplicit-api=strict"] 60 | } 61 | 62 | apiValidation { 63 | 64 | nonPublicMarkers += [ 65 | "kotlin.PublishedApi", 66 | ] 67 | } 68 | 69 | dependencies { 70 | implementation "androidx.appcompat:appcompat:$versions.androidxAppcompat" 71 | implementation "androidx.fragment:fragment-ktx:$versions.androidxFragment" 72 | implementation "androidx.core:core-ktx:$versions.ktx" 73 | implementation "com.google.android.material:material:$versions.googleMaterial" 74 | implementation "androidx.lifecycle:lifecycle-common-java8:2.4.1" 75 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" 76 | 77 | } 78 | 79 | dokkaHtml.configure { 80 | dokkaSourceSets { 81 | named("main") { 82 | noAndroidSdkLink.set(false) 83 | } 84 | } 85 | } 86 | 87 | 88 | allprojects { 89 | plugins.withId("com.vanniktech.maven.publish") { 90 | mavenPublish { 91 | sonatypeHost = "S01" 92 | } 93 | } 94 | } 95 | 96 | apply plugin: "com.vanniktech.maven.publish" 97 | apply from: "$rootDir/spotless.gradle" 98 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_artist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 25 | 26 | 27 | 28 | 36 | 37 | 46 | 47 | 54 | 55 | 64 | 65 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 30 | 31 | 36 | 39 | 40 | 41 | 42 | 47 | 50 | 53 | 56 | 57 | 62 | 63 | 64 | 68 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # /* 4 | # * Copyright 2022 AndroidPoet (Ranbir Singh) 5 | # * 6 | # * Licensed under the Apache License, Version 2.0 (the "License"); 7 | # * you may not use this file except in compliance with the License. 8 | # * You may obtain a copy of the License at 9 | # * 10 | # * http://www.apache.org/licenses/LICENSE-2.0 11 | # * 12 | # * Unless required by applicable law or agreed to in writing, software 13 | # * distributed under the License is distributed on an "AS IS" BASIS, 14 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # * See the License for the specific language governing permissions and 16 | # * limitations under the License. 17 | # */ 18 | # 19 | # 20 | 21 | # Project-wide Gradle settings. 22 | # IDE (e.g. Android Studio) users: 23 | # Gradle settings configured through the IDE *will override* 24 | # any settings specified in this file. 25 | # For more details on how to configure your build environment visit 26 | # http://www.gradle.org/docs/current/userguide/build_environment.html 27 | # Specifies the JVM arguments used for the daemon process. 28 | # The setting is particularly useful for tweaking memory settings. 29 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 30 | # When configured, Gradle will run in incubating parallel mode. 31 | # This option should only be used with decoupled projects. More details, visit 32 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 33 | # org.gradle.parallel=true 34 | # AndroidX package structure to make it clearer which packages are bundled with the 35 | # Android operating system, and which are packaged with your app"s APK 36 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 37 | android.useAndroidX=true 38 | # Kotlin code style for this project: "official" or "obsolete": 39 | kotlin.code.style=official 40 | # Enables namespacing of each library's R class so that its R class includes only the 41 | # resources declared in the library itself and none from the library's dependencies, 42 | # thereby reducing the size of the R class for that library 43 | android.nonTransitiveRClass=true 44 | 45 | # Required to publish to Nexus 46 | systemProp.org.gradle.internal.publish.checksums.insecure=true 47 | 48 | # Increase timeout when pushing to Sonatype 49 | systemProp.org.gradle.internal.http.connectionTimeout=120000 50 | systemProp.org.gradle.internal.http.socketTimeout=120000 51 | 52 | # Maven 53 | GROUP=io.github.androidpoet 54 | POM_PACKAGING=aar 55 | 56 | VERSION_NAME=1.1.6 57 | 58 | POM_ARTIFACT_ID=metaphor 59 | POM_NAME=metaphor 60 | POM_DESCRIPTION=Metaphor is the library to easily add Material Motion animation. 61 | 62 | POM_URL=https://github.com/AndroidPoet/Metaphor/ 63 | POM_SCM_URL=https://github.com/AndroidPoet/Metaphor/ 64 | POM_SCM_CONNECTION=scm:git:git://github.com/AndroidPoet/Metaphor.git 65 | POM_SCM_DEV_CONNECTION=scm:git:git://github.com/AndroidPoet/Metaphor.git 66 | 67 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 68 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 69 | POM_LICENCE_DIST=repo 70 | 71 | POM_DEVELOPER_ID=ranbirk66 72 | POM_DEVELOPER_NAME=Ranbir Singh 73 | POM_DEVELOPER_URL=https://github.com/AndroidPoet/ 74 | 75 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorView.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.view.View 5 | import androidx.annotation.MainThread 6 | import androidx.transition.ArcMotion 7 | import androidx.transition.PathMotion 8 | 9 | @DslMarker 10 | internal annotation class MetaphorViewInlineDsl 11 | 12 | /** 13 | * Creates an instance of the [MetaphorView] by scope of the [MetaphorView.Builder] using kotlin dsl. 14 | * 15 | * @param view A context for creating resources of the [MetaphorView]. 16 | * @param block A dsl scope lambda from the [MetaphorView.Builder]. 17 | * */ 18 | @MainThread 19 | @JvmSynthetic 20 | @MetaphorViewInlineDsl 21 | public inline fun metaphorView( 22 | view: View, 23 | crossinline block: MetaphorView.Builder.() -> Unit 24 | ): MetaphorView = 25 | MetaphorView.Builder(view).apply(block).build() 26 | 27 | /** 28 | * MetaphorFragment implements material motion animations. 29 | * 30 | * @see [MetaphorView](https://github.com/AndroidPoet/Metaphor) 31 | * 32 | * @param builder A [MetaphorView.Builder] for creating an instance of the [MetaphorView]. 33 | */ 34 | public class MetaphorView private constructor( 35 | builder: Builder 36 | ) { 37 | 38 | /** end view to transform into View */ 39 | public val endView: View? = builder.endView 40 | 41 | /** duration of the animations. */ 42 | public val duration: Long = builder.duration 43 | 44 | /** Animation of View. */ 45 | public val animation: MetaphorAnimation = builder.animation 46 | 47 | /** Motion path of on View animation */ 48 | public val motion: PathMotion = builder.motion 49 | 50 | /** start view to transform into View */ 51 | private val starView: View = builder.startView 52 | 53 | /** Builder class for [MetaphorView]. */ 54 | @MetaphorViewInlineDsl 55 | public class Builder(public val startView: View) { 56 | 57 | @set:JvmSynthetic 58 | public var duration: Long = 300 59 | 60 | @set:JvmSynthetic 61 | public var animation: MetaphorAnimation = MetaphorAnimation.FadeThrough 62 | 63 | @set:JvmSynthetic 64 | public var endView: View? = null 65 | 66 | @set:JvmSynthetic 67 | public var motion: PathMotion = ArcMotion() 68 | 69 | /** sets the duration of the View. */ 70 | public fun setDuration(value: Long): Builder = apply { this.duration = value } 71 | 72 | /** sets the animation of the animation. */ 73 | public fun setMetaphorAnimation(value: MetaphorAnimation): Builder = 74 | apply { this.animation = value } 75 | 76 | /** sets the endView of the animation. */ 77 | public fun setEndView(value: View): Builder = 78 | apply { this.endView = value } 79 | 80 | /** sets the motion of the animation. */ 81 | public fun setMotion(value: PathMotion): Builder = 82 | apply { this.motion = value } 83 | 84 | public fun build(): MetaphorView = MetaphorView(this) 85 | } 86 | /** starts animation. */ 87 | @MainThread 88 | public fun animate() { 89 | starView.applyAnimation(this) 90 | } 91 | 92 | public abstract class Factory { 93 | 94 | /** 95 | * Creates a new instance of [MetaphorView]. 96 | * 97 | * @return A new created instance of the [MetaphorView]. 98 | */ 99 | public abstract fun create(view: View): MetaphorView 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/ActivityExtenstions.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.app.Activity 5 | import android.graphics.Color 6 | import android.os.Build 7 | import androidx.annotation.RequiresApi 8 | import androidx.interpolator.view.animation.FastOutSlowInInterpolator 9 | import com.google.android.material.transition.platform.MaterialContainerTransform 10 | import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback 11 | 12 | /** applies Animation form attributes to a View instance. */ 13 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 14 | @JvmSynthetic 15 | internal fun Activity.applyAnimation( 16 | metaphor: MetaphorActivity 17 | ) { 18 | val enterAnimation = 19 | getWindowMetaphorAnimation(metaphor.enterAnimation)?.let { 20 | addAnimationProperties( 21 | it, 22 | metaphor, 23 | metaphor.enterDuration 24 | ) 25 | } 26 | val exitAnimation = 27 | getWindowMetaphorAnimation(metaphor.exitAnimation)?.let { 28 | addAnimationProperties( 29 | it, 30 | metaphor, 31 | metaphor.exitDuration 32 | ) 33 | } 34 | val reenterAnimation = 35 | getWindowMetaphorAnimation(metaphor.reenterAnimation)?.let { 36 | addAnimationProperties( 37 | it, 38 | metaphor, 39 | metaphor.reenterDuration 40 | ) 41 | } 42 | val returnAnimation = 43 | getWindowMetaphorAnimation(metaphor.returnAnimation)?.let { 44 | addAnimationProperties( 45 | it, 46 | metaphor, 47 | metaphor.returnDuration 48 | ) 49 | } 50 | 51 | window.enterTransition = enterAnimation 52 | window.exitTransition = exitAnimation 53 | window.reenterTransition = reenterAnimation 54 | window.returnTransition = returnAnimation 55 | } 56 | 57 | /** applies Properties on Animation form attributes. */ 58 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 59 | @JvmSynthetic 60 | internal fun Activity.addAnimationProperties( 61 | transition: android.transition.Transition, 62 | metaphor: MetaphorActivity, 63 | animationDuration: Long 64 | ): android.transition.Transition { 65 | 66 | if (transition is MaterialContainerTransform) { 67 | 68 | if (metaphor.startActivity) { 69 | setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback()) 70 | } else { 71 | setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) 72 | metaphor.view?.transitionName = metaphor.transitionName 73 | 74 | transition.apply { 75 | interpolator = FastOutSlowInInterpolator() 76 | fadeMode = MaterialContainerTransform.FADE_MODE_IN 77 | } 78 | window.sharedElementEnterTransition = transition.apply { 79 | setAllContainerColors(Color.TRANSPARENT) 80 | scrimColor = Color.TRANSPARENT 81 | } 82 | window.sharedElementReturnTransition = transition.apply { 83 | setAllContainerColors(Color.TRANSPARENT) 84 | scrimColor = Color.TRANSPARENT 85 | } 86 | } 87 | } 88 | transition.apply { 89 | duration = animationDuration 90 | pathMotion = metaphor.motion 91 | 92 | addTarget(metaphor.view) 93 | window.allowEnterTransitionOverlap = metaphor.enterTransitionOverlap 94 | window.allowReturnTransitionOverlap = metaphor.returnTransitionOverlap 95 | } 96 | return transition 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/activties/FragmentHostActivty.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.activties 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.View 7 | import android.widget.FrameLayout 8 | import androidx.annotation.RequiresApi 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.core.view.ViewCompat 11 | import androidx.core.view.WindowInsetsCompat 12 | import androidx.lifecycle.lifecycleScope 13 | import androidx.navigation.findNavController 14 | import androidx.navigation.ui.AppBarConfiguration 15 | import androidx.navigation.ui.setupWithNavController 16 | import com.androidpoet.metaphor.hide 17 | import com.androidpoet.metaphor.show 18 | import com.androidpoet.metaphordemo.R 19 | import com.androidpoet.metaphordemo.databinding.ActivityHostActivtyBinding 20 | import com.google.android.material.bottomnavigation.BottomNavigationView 21 | 22 | class FragmentHostActivty : AppCompatActivity() { 23 | 24 | private lateinit var binding: ActivityHostActivtyBinding 25 | 26 | @RequiresApi(Build.VERSION_CODES.M) 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | binding = ActivityHostActivtyBinding.inflate(layoutInflater) 30 | setContentView(binding.container) 31 | 32 | val navView: BottomNavigationView = binding.navView 33 | 34 | val navController = findNavController(R.id.nav_host_fragment_activity_host_activty) 35 | // Passing each menu ID as a set of Ids because each 36 | // menu should be considered as top level destinations. 37 | val appBarConfiguration = AppBarConfiguration( 38 | setOf( 39 | R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications 40 | ) 41 | ) 42 | navView.setupWithNavController(navController) 43 | // Hide bottom nav on screens which don't require it 44 | lifecycleScope.launchWhenResumed { 45 | navController.addOnDestinationChangedListener { _, destination, _ -> 46 | when (destination.id) { 47 | R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications -> navView.show() 48 | 49 | else -> navView.hide() 50 | } 51 | } 52 | } 53 | if (Build.VERSION.SDK_INT >= 30) { 54 | 55 | // Root ViewGroup of my activity 56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 57 | val decor: View = window.decorView 58 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) 59 | } 60 | ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets -> 61 | 62 | val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 63 | 64 | // Apply the insets as a margin to the view. Here the system is setting 65 | // only the bottom, left, and right dimensions, but apply whichever insets are 66 | // appropriate to your layout. You can also update the view padding 67 | // if that's more appropriate. 68 | 69 | view.layoutParams = (view.layoutParams as FrameLayout.LayoutParams).apply { 70 | leftMargin = insets.left 71 | bottomMargin = insets.bottom 72 | rightMargin = insets.right 73 | } 74 | 75 | // Return CONSUMED if you don't want want the window insets to keep being 76 | // passed down to descendant views. 77 | WindowInsetsCompat.CONSUMED 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_add_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 22 | 23 | 28 | 29 | 33 | 34 | 35 | 50 | 51 | 65 | 66 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 25 | 26 | 32 | 33 | 34 | 38 | 39 | 48 | 49 | 55 | 56 | 65 | 66 | 74 | 75 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dashboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 29 | 33 | 34 | 43 | 44 | 50 | 51 | 60 | 61 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/res/layout/artist_linear_list.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 27 | 28 | 29 | 34 | 35 | 40 | 41 | 48 | 49 | 59 | 60 | 61 | 62 | 72 | 73 | 81 | 82 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/notifications/NotificationsFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.notifications 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.fragment.app.Fragment 11 | import com.androidpoet.metaphor.MetaphorAnimation 12 | import com.androidpoet.metaphor.MetaphorFragment 13 | import com.androidpoet.metaphor.MetaphorView 14 | import com.androidpoet.metaphordemo.R 15 | import com.androidpoet.metaphordemo.databinding.FragmentNotificationsBinding 16 | import com.bumptech.glide.Glide 17 | import java.util.Random 18 | 19 | class NotificationsFragment : Fragment() { 20 | 21 | private var _binding: FragmentNotificationsBinding? = null 22 | 23 | // This property is only valid between onCreateView and 24 | // onDestroyView. 25 | private val binding get() = _binding!! 26 | 27 | private val images = arrayListOf( 28 | R.drawable.undraw_drag, 29 | R.drawable.undraw_winners, 30 | R.drawable.undraw_social_sharing, 31 | R.drawable.undraw_drag, 32 | R.drawable.undraw_winners, 33 | R.drawable.undraw_social_sharing, 34 | R.drawable.undraw_drag, 35 | R.drawable.undraw_winners, 36 | R.drawable.undraw_social_sharing, 37 | R.drawable.undraw_winners 38 | ) 39 | 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | // FadeThrough inside fragment 43 | val meta = MetaphorFragment.Builder(this) 44 | .setEnterAnimation(MetaphorAnimation.ElevationScale) 45 | .setExitAnimation(MetaphorAnimation.ElevationScaleGrow) 46 | .build() 47 | meta.animate() 48 | } 49 | 50 | override fun onCreateView( 51 | inflater: LayoutInflater, 52 | container: ViewGroup?, 53 | savedInstanceState: Bundle? 54 | ): View { 55 | 56 | _binding = FragmentNotificationsBinding.inflate(inflater, container, false) 57 | 58 | return binding.root 59 | } 60 | 61 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 62 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 63 | super.onViewCreated(view, savedInstanceState) 64 | 65 | Glide.with(requireContext()).load(getRandomItem(images)).into(binding.img) 66 | Glide.with(requireContext()).load(getRandomItem(images)).into(binding.img2) 67 | Glide.with(requireContext()).load(getRandomItem(images)).into(binding.img3) 68 | 69 | binding.SharedX.setOnClickListener { 70 | 71 | val meta = MetaphorView.Builder(binding.img) 72 | .setDuration(1000) 73 | .setEndView(binding.img) 74 | .setMetaphorAnimation(MetaphorAnimation.Fade) 75 | .build() 76 | meta.animate() 77 | } 78 | 79 | binding.SharedY.setOnClickListener { 80 | 81 | val meta = MetaphorView.Builder(binding.img) 82 | .setDuration(1000) 83 | .setEndView(binding.img) 84 | .setMetaphorAnimation(MetaphorAnimation.Fade) 85 | .build() 86 | meta.animate() 87 | } 88 | binding.SharedZ.setOnClickListener { 89 | 90 | val meta = MetaphorView.Builder(binding.img) 91 | .setDuration(1000) 92 | .setEndView(binding.img) 93 | .setMetaphorAnimation(MetaphorAnimation.FadeThrough) 94 | .build() 95 | meta.animate() 96 | } 97 | 98 | binding.materialFadeThrough.setOnClickListener { 99 | 100 | val meta = MetaphorView.Builder(binding.img2) 101 | .setDuration(1000) 102 | .setEndView(binding.img2) 103 | .setMetaphorAnimation(MetaphorAnimation.FadeThrough) 104 | .build() 105 | meta.animate() 106 | } 107 | 108 | binding.materialFade.setOnClickListener { 109 | 110 | Glide.with(requireContext()).load(getRandomItem(images)).into(binding.img3) 111 | val meta = MetaphorView.Builder(binding.img3) 112 | .setDuration(1000) 113 | .setEndView(binding.img3) 114 | .setMetaphorAnimation(MetaphorAnimation.FadeThrough) 115 | .build() 116 | meta.animate() 117 | } 118 | } 119 | 120 | override fun onDestroyView() { 121 | super.onDestroyView() 122 | 123 | _binding = null 124 | } 125 | 126 | private fun getRandomItem(list: List): T { 127 | val random = Random() 128 | val listSize = list.size 129 | val randomIndex: Int = random.nextInt(listSize) 130 | return list[randomIndex] 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistDetailFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.graphics.drawable.Drawable 5 | import android.os.Build 6 | import android.os.Bundle 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import androidx.annotation.RequiresApi 11 | import androidx.fragment.app.Fragment 12 | import androidx.navigation.fragment.findNavController 13 | import androidx.navigation.fragment.navArgs 14 | import com.androidpoet.metaphor.MetaphorAnimation 15 | import com.androidpoet.metaphor.MetaphorFragment 16 | import com.androidpoet.metaphor.MetaphorView 17 | import com.androidpoet.metaphor.metaphorView 18 | import com.androidpoet.metaphordemo.R 19 | import com.androidpoet.metaphordemo.databinding.FragmentArtistDetailBinding 20 | import com.bumptech.glide.Glide 21 | import com.bumptech.glide.Priority 22 | import com.bumptech.glide.load.DataSource 23 | import com.bumptech.glide.load.engine.GlideException 24 | import com.bumptech.glide.request.RequestListener 25 | import com.bumptech.glide.request.target.Target 26 | import com.google.android.material.transition.MaterialArcMotion 27 | 28 | class ArtistDetailFragment : Fragment() { 29 | 30 | val args: ArtistDetailFragmentArgs by navArgs() 31 | private lateinit var viewBinding: FragmentArtistDetailBinding 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | } 36 | 37 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 38 | override fun onCreateView( 39 | inflater: LayoutInflater, 40 | container: ViewGroup?, 41 | savedInstanceState: Bundle? 42 | ): View { 43 | viewBinding = FragmentArtistDetailBinding.inflate(inflater, container, false).apply { 44 | lifecycleOwner = viewLifecycleOwner 45 | } 46 | return viewBinding.root 47 | } 48 | 49 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 50 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 51 | super.onViewCreated(view, savedInstanceState) 52 | 53 | /** This is the method to perform MaterialContainerTransform which we need to call inside onViewCreated 54 | * it is important to call this method inside onViewCreated */ 55 | // metaphorDestinationFragmentMaterialContainerTransform(view, args.data.pos.toString()) 56 | 57 | val metaphor = MetaphorFragment.Builder(this) 58 | .setEnterDuration(1000) 59 | .setView(view) 60 | .setTransitionName(args.data.pos.toString()) 61 | .setExitAnimation(MetaphorAnimation.ContainerTransform) 62 | .setMotion(MaterialArcMotion()) 63 | .build() 64 | metaphor.animate() 65 | 66 | viewBinding.toolBar.setNavigationIcon(R.drawable.ic_baseline_arrow_back_24); // your drawable 67 | viewBinding.toolBar.setNavigationOnClickListener( 68 | View.OnClickListener { 69 | // back button pressed 70 | findNavController().popBackStack() 71 | } 72 | ) 73 | 74 | viewBinding.fabDetail.setOnClickListener { 75 | 76 | val meta = metaphorView(viewBinding.fabDetail) { 77 | setDuration(1000) 78 | setEndView(viewBinding.controls) 79 | setMetaphorAnimation(MetaphorAnimation.ContainerTransform) 80 | setMotion(MaterialArcMotion()) 81 | build() 82 | } 83 | 84 | meta.animate() 85 | } 86 | 87 | viewBinding.controls.setOnClickListener { 88 | // it is reference for the currant view 89 | // params[endView]you need to pass end view for the transformation 90 | val meta = MetaphorView.Builder(it) 91 | .setDuration(1000) 92 | .setEndView(viewBinding.fabDetail) 93 | .setMetaphorAnimation(MetaphorAnimation.ContainerTransform) 94 | .setMotion(MaterialArcMotion()) 95 | .build() 96 | meta.animate() 97 | } 98 | 99 | // load image with palette 100 | Glide.with(requireContext()) 101 | .load(args.data.img) 102 | .thumbnail( 103 | Glide.with(requireContext()) 104 | .load(args.data.img) 105 | .priority(Priority.IMMEDIATE) 106 | 107 | ) 108 | .addListener(object : RequestListener { 109 | override fun onLoadFailed( 110 | e: GlideException?, 111 | model: Any?, 112 | target: Target?, 113 | isFirstResource: Boolean 114 | ): Boolean { 115 | return false 116 | } 117 | 118 | override fun onResourceReady( 119 | resource: Drawable?, 120 | model: Any?, 121 | target: Target?, 122 | dataSource: DataSource?, 123 | isFirstResource: Boolean 124 | ): Boolean { 125 | 126 | return false 127 | } 128 | }) 129 | .into(viewBinding.artistImageView) 130 | } 131 | 132 | override fun onDestroyView() { 133 | 134 | super.onDestroyView() 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorWindow.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.view.View 5 | import android.widget.PopupWindow 6 | import androidx.annotation.MainThread 7 | 8 | @DslMarker 9 | internal annotation class MetaphorWindowInlineDsl 10 | 11 | /** 12 | * Creates an instance of the [MetaphorWindow] by scope of the [MetaphorWindow.Builder] using kotlin dsl. 13 | * 14 | * @param activity A context for creating resources of the [MetaphorWindow]. 15 | * @param block A dsl scope lambda from the [MetaphorWindow.Builder]. 16 | * */ 17 | @MainThread 18 | @JvmSynthetic 19 | @MetaphorWindowInlineDsl 20 | public inline fun metaphorWindow( 21 | popupWindow: PopupWindow, 22 | crossinline block: MetaphorWindow.Builder.() -> Unit 23 | ): MetaphorWindow = 24 | MetaphorWindow.Builder(popupWindow).apply(block).build() 25 | 26 | /** 27 | * MetaphorActivity implements material motion animations. 28 | * 29 | * @see [MetaphorWindow](https://github.com/AndroidPoet/Metaphor) 30 | * 31 | * @param builder A [MetaphorWindow.Builder] for creating an instance of the [MetaphorWindow]. 32 | */ 33 | public class MetaphorWindow private constructor( 34 | builder: Builder 35 | ) { 36 | /** duration of enter the animations. */ 37 | public val enterDuration: Long = builder.enterDuration 38 | 39 | /** duration of exit the animations. */ 40 | public val exitDuration: Long = builder.exitDuration 41 | 42 | /** Enter AnimationOverlap of Activity. */ 43 | public val enterTransitionOverlap: Boolean = builder.enterTransitionOverlap 44 | 45 | /** Return AnimationOverlap of Activity. */ 46 | public val returnTransitionOverlap: Boolean = builder.returnTransitionOverlap 47 | 48 | /** Enter Animation of fragment. */ 49 | public var enterAnimation: MetaphorAnimation = builder.enterAnimation 50 | 51 | /** Exit Animation of fragment. */ 52 | public var exitAnimation: MetaphorAnimation = builder.exitAnimation 53 | 54 | /** Motion path of on fragment animation */ 55 | public val motion: android.transition.PathMotion = builder.motion 56 | 57 | /** Fragment on which animation will apply */ 58 | public val window: PopupWindow = builder.popupWindow 59 | 60 | /** Builder class for [MetaphorFragment]. */ 61 | @MetaphorViewInlineDsl 62 | public class Builder(public val popupWindow: PopupWindow) { 63 | 64 | @set:JvmSynthetic 65 | public var enterDuration: Long = 300 66 | 67 | @set:JvmSynthetic 68 | public var reenterDuration: Long = 300 69 | 70 | @set:JvmSynthetic 71 | public var exitDuration: Long = 300 72 | 73 | @set:JvmSynthetic 74 | public var returnDuration: Long = 300 75 | 76 | @set:JvmSynthetic 77 | public var enterAnimation: MetaphorAnimation = MetaphorAnimation.None 78 | 79 | @set:JvmSynthetic 80 | public var exitAnimation: MetaphorAnimation = MetaphorAnimation.None 81 | 82 | @set:JvmSynthetic 83 | public var motion: android.transition.PathMotion = android.transition.ArcMotion() 84 | 85 | @set:JvmSynthetic 86 | public var view: View? = null 87 | 88 | @set:JvmSynthetic 89 | public var transitionName: String = "" 90 | 91 | @set:JvmSynthetic 92 | public var enterTransitionOverlap: Boolean = false 93 | 94 | @set:JvmSynthetic 95 | public var returnTransitionOverlap: Boolean = false 96 | 97 | /** sets the duration of the Animation. */ 98 | public fun setEnterDuration(value: Long): Builder = apply { this.enterDuration = value } 99 | 100 | /** sets the duration of the Animation. */ 101 | public fun setExitDuration(value: Long): Builder = apply { this.exitDuration = value } 102 | 103 | /** sets enter the animation of the Activity. */ 104 | public fun setEnterAnimation(value: MetaphorAnimation): Builder = 105 | apply { this.enterAnimation = value } 106 | 107 | /** sets the exit animation of the Activity. */ 108 | public fun setExitAnimation(value: MetaphorAnimation): Builder = 109 | apply { this.exitAnimation = value } 110 | 111 | /** sets the view of the Activity. */ 112 | public fun setView(value: View): Builder = apply { this.view = value } 113 | 114 | /** sets the motion of the View. */ 115 | public fun setMotion(value: android.transition.PathMotion): Builder = 116 | apply { this.motion = value } 117 | 118 | /** sets the transition of the View. */ 119 | public fun setTransitionName(value: String): Builder = apply { this.transitionName = value } 120 | 121 | /** sets the enter overlap of the Activity. */ 122 | public fun setEnterOverlap(value: Boolean): Builder = 123 | apply { this.enterTransitionOverlap = value } 124 | 125 | /** sets the return overlap of the Activity. */ 126 | public fun setReturnOverlap(value: Boolean): Builder = 127 | apply { this.returnTransitionOverlap = value } 128 | 129 | public fun build(): MetaphorWindow = MetaphorWindow(this) 130 | } 131 | 132 | /** starts animation. */ 133 | public fun animate() { 134 | window.applyAnimation(this) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/home/ArtistListFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.home 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.fragment.app.Fragment 11 | import androidx.lifecycle.lifecycleScope 12 | import androidx.navigation.fragment.FragmentNavigatorExtras 13 | import androidx.navigation.fragment.findNavController 14 | import androidx.recyclerview.widget.GridLayoutManager 15 | import androidx.recyclerview.widget.LinearLayoutManager 16 | import com.androidpoet.metaphor.MetaphorAnimation 17 | import com.androidpoet.metaphor.hold 18 | import com.androidpoet.metaphor.metaphorFragment 19 | import com.androidpoet.metaphordemo.R 20 | import com.androidpoet.metaphordemo.databinding.FragmentListBinding 21 | import com.bumptech.glide.Glide 22 | import com.google.gson.Gson 23 | import com.google.gson.reflect.TypeToken 24 | 25 | class ArtistListFragment : Fragment() { 26 | 27 | private lateinit var artistGridListAdapter: ArtistGridListAdapter 28 | private lateinit var artistLinearListAdapter: ArtistLinearListAdapter 29 | private lateinit var viewBinding: FragmentListBinding 30 | 31 | private var isGrid: Boolean = true 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | 36 | val meta = metaphorFragment(this) { 37 | setExitAnimation(MetaphorAnimation.ElevationScaleGrow) 38 | setReenterAnimation(MetaphorAnimation.ElevationScale) 39 | 40 | build() 41 | } 42 | meta.animate() 43 | 44 | artistGridListAdapter = ArtistGridListAdapter(requireContext(), Glide.with(requireContext())) 45 | artistLinearListAdapter = 46 | ArtistLinearListAdapter(requireContext(), Glide.with(requireContext())) 47 | artistGridListAdapter.callback = object : ArtistGridListAdapter.Callback { 48 | override fun onClick(view: View, item: SampleResponse, imageUrl: String) { 49 | /**this method is used for MaterialContainerTransform it add MaterialElevationScale 50 | * to components] */ 51 | 52 | val extras = FragmentNavigatorExtras(view to item.pos.toString()) 53 | val action = ArtistListFragmentDirections.navToCharacterDetailFragment(item) 54 | findNavController().navigate(action, extras) 55 | } 56 | } 57 | 58 | artistLinearListAdapter.callback = object : ArtistLinearListAdapter.Callback { 59 | override fun onClick(view: View, item: SampleResponse, imageUrl: String) { 60 | /**this method is used for MaterialContainerTransform it add MaterialElevationScale 61 | * to components] */ 62 | 63 | val extras = FragmentNavigatorExtras(view to item.pos.toString()) 64 | val action = ArtistListFragmentDirections.navToCharacterDetailFragment(item) 65 | findNavController().navigate(action, extras) 66 | } 67 | } 68 | } 69 | 70 | private fun sampleResponse(): List { 71 | val response = resources.openRawResource(R.raw.grid).bufferedReader() 72 | .use { it.readText() } 73 | return Gson().fromJson(response, object : TypeToken>() {}.type) 74 | } 75 | 76 | override fun onCreateView( 77 | inflater: LayoutInflater, 78 | container: ViewGroup?, 79 | savedInstanceState: Bundle? 80 | ): View { 81 | viewBinding = FragmentListBinding.inflate(inflater, container, false).apply { 82 | lifecycleOwner = viewLifecycleOwner 83 | } 84 | return viewBinding.root 85 | } 86 | 87 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 88 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 89 | super.onViewCreated(view, savedInstanceState) 90 | /**this method is used for MaterialContainerTransform it add some delay to load animation basically it will wait for recyclerview to be drawn */ 91 | hold() 92 | 93 | loadRecyclerView(isGrid) 94 | viewBinding.reorder.setOnClickListener { 95 | 96 | if (isGrid) { 97 | isGrid = false 98 | loadRecyclerView(isGrid) 99 | } else { 100 | isGrid = true 101 | 102 | loadRecyclerView(isGrid) 103 | } 104 | } 105 | } 106 | 107 | fun loadRecyclerView(isGrid: Boolean) { 108 | 109 | if (isGrid) { 110 | viewBinding.rcv.apply { 111 | 112 | layoutManager = GridLayoutManager(requireContext(), 2) 113 | adapter = artistGridListAdapter.apply { 114 | viewLifecycleOwner.lifecycleScope.launchWhenStarted { 115 | submitList(sampleResponse()) 116 | } 117 | } 118 | } 119 | } else { 120 | viewBinding.rcv.apply { 121 | 122 | layoutManager = LinearLayoutManager(requireContext()) 123 | adapter = artistLinearListAdapter.apply { 124 | viewLifecycleOwner.lifecycleScope.launchWhenStarted { 125 | submitList(sampleResponse()) 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidpoet/metaphordemo/ui/dashboard/DashboardFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphordemo.ui.dashboard 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.annotation.RequiresApi 10 | import androidx.fragment.app.Fragment 11 | import androidx.lifecycle.lifecycleScope 12 | import androidx.navigation.fragment.FragmentNavigatorExtras 13 | import androidx.navigation.fragment.findNavController 14 | import androidx.recyclerview.widget.GridLayoutManager 15 | import androidx.recyclerview.widget.LinearLayoutManager 16 | import com.androidpoet.metaphor.metaphorFragment 17 | import com.androidpoet.metaphordemo.R 18 | import com.androidpoet.metaphordemo.databinding.FragmentDashboardBinding 19 | import com.androidpoet.metaphordemo.factory.MetaphorFragmentFactory 20 | import com.androidpoet.metaphordemo.ui.home.ArtistGridListAdapter 21 | import com.androidpoet.metaphordemo.ui.home.ArtistLinearListAdapter 22 | import com.androidpoet.metaphordemo.ui.home.SampleResponse 23 | import com.bumptech.glide.Glide 24 | import com.google.gson.Gson 25 | import com.google.gson.reflect.TypeToken 26 | 27 | class DashboardFragment : Fragment() { 28 | 29 | private var _binding: FragmentDashboardBinding? = null 30 | 31 | // This property is only valid between onCreateView and 32 | // onDestroyView. 33 | private val viewBinding get() = _binding!! 34 | private lateinit var artistGridListAdapter: ArtistGridListAdapter 35 | private lateinit var artistLinearListAdapter: ArtistLinearListAdapter 36 | 37 | private var isGrid: Boolean = true 38 | private val metaphorFragment by metaphorFragment() 39 | override fun onCreate(savedInstanceState: Bundle?) { 40 | super.onCreate(savedInstanceState) 41 | metaphorFragment.animate() 42 | 43 | artistGridListAdapter = ArtistGridListAdapter(requireContext(), Glide.with(requireContext())) 44 | artistLinearListAdapter = 45 | ArtistLinearListAdapter(requireContext(), Glide.with(requireContext())) 46 | artistGridListAdapter.callback = object : ArtistGridListAdapter.Callback { 47 | override fun onClick(view: View, item: SampleResponse, imageUrl: String) { 48 | /**this method is used for MaterialContainerTransform it add MaterialElevationScale 49 | * to components] */ 50 | 51 | val extras = FragmentNavigatorExtras(view to item.pos.toString()) 52 | val action = DashboardFragmentDirections.navToCharacterDetailFragment(item) 53 | findNavController().navigate(action, extras) 54 | } 55 | } 56 | 57 | artistLinearListAdapter.callback = object : ArtistLinearListAdapter.Callback { 58 | override fun onClick(view: View, item: SampleResponse, imageUrl: String) { 59 | /**this method is used for MaterialContainerTransform it add MaterialElevationScale 60 | * to components] */ 61 | 62 | val extras = FragmentNavigatorExtras(view to item.pos.toString()) 63 | val action = DashboardFragmentDirections.navToCharacterDetailFragment(item) 64 | findNavController().navigate(action, extras) 65 | } 66 | } 67 | } 68 | 69 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 70 | override fun onCreateView( 71 | inflater: LayoutInflater, 72 | container: ViewGroup?, 73 | savedInstanceState: Bundle? 74 | ): View { 75 | 76 | _binding = FragmentDashboardBinding.inflate(inflater, container, false) 77 | val root: View = viewBinding.root 78 | 79 | viewBinding.fab.setOnClickListener { 80 | val action = DashboardFragmentDirections.actionNavigationHomeToAddnoteFragment() 81 | findNavController().navigate(action) 82 | } 83 | 84 | return root 85 | } 86 | 87 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 88 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 89 | super.onViewCreated(view, savedInstanceState) 90 | loadRecyclerView(isGrid) 91 | viewBinding.reorder.setOnClickListener { 92 | 93 | if (isGrid) { 94 | isGrid = false 95 | 96 | loadRecyclerView(isGrid) 97 | } else { 98 | isGrid = true 99 | 100 | loadRecyclerView(isGrid) 101 | } 102 | } 103 | } 104 | 105 | private fun loadRecyclerView(isGrid: Boolean) { 106 | 107 | if (isGrid) { 108 | viewBinding.rcv.apply { 109 | layoutManager = GridLayoutManager(requireContext(), 2) 110 | adapter = artistGridListAdapter.apply { 111 | viewLifecycleOwner.lifecycleScope.launchWhenStarted { 112 | submitList(sampleResponse()) 113 | } 114 | } 115 | } 116 | } else { 117 | viewBinding.rcv.apply { 118 | layoutManager = LinearLayoutManager(requireContext()) 119 | adapter = artistLinearListAdapter.apply { 120 | viewLifecycleOwner.lifecycleScope.launchWhenStarted { 121 | 122 | submitList(sampleResponse()) 123 | } 124 | } 125 | } 126 | } 127 | } 128 | 129 | override fun onDestroyView() { 130 | super.onDestroyView() 131 | _binding = null 132 | } 133 | 134 | private fun sampleResponse(): List { 135 | val response = resources.openRawResource(R.raw.list).bufferedReader() 136 | .use { it.readText() } 137 | return Gson().fromJson(response, object : TypeToken>() {}.type) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_notifications.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 29 | 30 | 34 | 35 | 36 | 39 | 40 | 45 | 46 | 47 | 48 | 49 | 57 | 58 | 75 | 76 | 91 | 92 | 108 | 109 | 110 | 111 | 115 | 116 | 117 | 128 | 129 | 133 | 134 | 135 | 136 | 137 | 138 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/ViewExtensions.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.animation.ValueAnimator 5 | import android.annotation.SuppressLint 6 | import android.graphics.Bitmap 7 | import android.graphics.Bitmap.Config.ARGB_8888 8 | import android.graphics.Color 9 | import android.graphics.drawable.BitmapDrawable 10 | import android.os.Build 11 | import android.view.View 12 | import android.view.View.GONE 13 | import android.view.View.VISIBLE 14 | import android.view.ViewGroup 15 | import android.view.animation.AnimationUtils 16 | import androidx.annotation.Px 17 | import androidx.annotation.RequiresApi 18 | import androidx.core.animation.doOnEnd 19 | import androidx.core.graphics.applyCanvas 20 | import androidx.core.view.ViewCompat 21 | import androidx.core.view.isVisible 22 | import androidx.transition.TransitionManager 23 | import com.google.android.material.bottomnavigation.BottomNavigationView 24 | import com.google.android.material.transition.MaterialContainerTransform 25 | 26 | /** applies Animation form attributes to a View instance. */ 27 | @JvmSynthetic 28 | internal fun View.applyAnimation( 29 | metaphor: MetaphorView 30 | ) { 31 | val parent = parent as? ViewGroup 32 | if (parent != null) { 33 | val transition = getMetaphorAnimation(metaphor.animation) 34 | if (transition != null) { 35 | transition.duration = metaphor.duration 36 | transition.setPathMotion(metaphor.motion) 37 | } 38 | 39 | if (transition is MaterialContainerTransform) { 40 | transition.scrimColor = Color.TRANSPARENT 41 | transition.startView = this 42 | transition.endView = metaphor.endView 43 | 44 | metaphor.endView?.let { transition.addTarget(it) } 45 | } 46 | TransitionManager.beginDelayedTransition( 47 | parent, 48 | transition 49 | ) 50 | 51 | // Make any changes to the hierarchy to be animated by the shared axis transition. 52 | 53 | if (this == metaphor.endView) { 54 | 55 | if (metaphor.endView.isVisible) { 56 | this.visibility = GONE 57 | } else { 58 | this.visibility = VISIBLE 59 | } 60 | } else { 61 | 62 | visibility = GONE 63 | if (metaphor.endView != null) { 64 | metaphor.endView.visibility = VISIBLE 65 | } 66 | } 67 | } 68 | } 69 | /** 70 | * Potentially animate showing a [BottomNavigationView]. 71 | * 72 | * Abruptly changing the visibility leads to a re-layout of main content, animating 73 | * `translationY` leaves a gap where the view was that content does not fill. 74 | * 75 | * Instead, take a snapshot of the view, and animate this in, only changing the visibility (and 76 | * thus layout) when the animation completes. 77 | */ 78 | @SuppressLint("NewApi") 79 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2) 80 | @JvmSynthetic 81 | public fun BottomNavigationView.show() { 82 | if (visibility == VISIBLE) return 83 | 84 | val parent = parent as ViewGroup 85 | // View needs to be laid out to create a snapshot & know position to animate. If view isn't 86 | // laid out yet, need to do this manually. 87 | if (!isLaidOut) { 88 | measure( 89 | View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY), 90 | View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.AT_MOST) 91 | ) 92 | layout(parent.left, parent.height - measuredHeight, parent.right, parent.height) 93 | } 94 | 95 | val drawable = BitmapDrawable(context.resources, drawToBitmap()) 96 | drawable.setBounds(left, parent.height, right, parent.height + height) 97 | parent.overlay.add(drawable) 98 | ValueAnimator.ofInt(parent.height, top).apply { 99 | startDelay = 100L 100 | duration = 300L 101 | interpolator = AnimationUtils.loadInterpolator( 102 | context, 103 | android.R.interpolator.linear_out_slow_in 104 | ) 105 | addUpdateListener { 106 | val newTop = it.animatedValue as Int 107 | drawable.setBounds(left, newTop, right, newTop + height) 108 | } 109 | doOnEnd { 110 | parent.overlay.remove(drawable) 111 | visibility = VISIBLE 112 | } 113 | start() 114 | } 115 | } 116 | 117 | /** 118 | * Potentially animate hiding a [BottomNavigationView]. 119 | * 120 | * Abruptly changing the visibility leads to a re-layout of main content, animating 121 | * `translationY` leaves a gap where the view was that content does not fill. 122 | * 123 | * Instead, take a snapshot, instantly hide the view (so content lays out to fill), then animate 124 | * out the snapshot. 125 | */ 126 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 127 | @JvmSynthetic 128 | public fun BottomNavigationView.hide() { 129 | if (visibility == GONE) return 130 | 131 | val drawable = BitmapDrawable(context.resources, drawToBitmap()) 132 | val parent = parent as ViewGroup 133 | drawable.setBounds(left, top, right, bottom) 134 | parent.overlay.add(drawable) 135 | visibility = GONE 136 | ValueAnimator.ofInt(top, parent.height).apply { 137 | startDelay = 100L 138 | duration = 200L 139 | interpolator = AnimationUtils.loadInterpolator( 140 | context, 141 | android.R.interpolator.fast_out_linear_in 142 | ) 143 | addUpdateListener { 144 | val newTop = it.animatedValue as Int 145 | drawable.setBounds(left, newTop, right, newTop + height) 146 | } 147 | doOnEnd { 148 | parent.overlay.remove(drawable) 149 | } 150 | start() 151 | } 152 | } 153 | 154 | /** 155 | * A copy of the KTX method, adding the ability to add extra padding the bottom of the [Bitmap]; 156 | * useful when it will be used in a [android.graphics.BitmapShader]with 157 | * a [android.graphics.Shader.TileMode.CLAMP][CLAMP tile mode]. 158 | */ 159 | @JvmSynthetic 160 | public fun View.drawToBitmap(@Px extraPaddingBottom: Int = 0): Bitmap { 161 | if (!ViewCompat.isLaidOut(this)) { 162 | throw IllegalStateException("View needs to be laid out before calling drawToBitmap()") 163 | } 164 | return Bitmap.createBitmap(width, height + extraPaddingBottom, ARGB_8888).applyCanvas { 165 | translate(-scrollX.toFloat(), -scrollY.toFloat()) 166 | draw(this) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/AnimationsExtenstions.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.os.Build 5 | import android.view.animation.Interpolator 6 | import android.view.animation.LinearInterpolator 7 | import android.view.animation.PathInterpolator 8 | import androidx.annotation.RequiresApi 9 | import androidx.core.graphics.PathParser 10 | import androidx.interpolator.view.animation.FastOutLinearInInterpolator 11 | import androidx.interpolator.view.animation.FastOutSlowInInterpolator 12 | import androidx.interpolator.view.animation.LinearOutSlowInInterpolator 13 | import androidx.transition.Transition 14 | import com.google.android.material.transition.MaterialContainerTransform 15 | import com.google.android.material.transition.MaterialElevationScale 16 | import com.google.android.material.transition.MaterialFade 17 | import com.google.android.material.transition.MaterialFadeThrough 18 | import com.google.android.material.transition.MaterialSharedAxis 19 | 20 | /** applies Metaphor form attributes to a View instance. */ 21 | @JvmSynthetic 22 | @PublishedApi 23 | internal fun getMetaphorAnimation(animation: MetaphorAnimation): Transition? { 24 | 25 | when (animation) { 26 | MetaphorAnimation.ContainerTransform -> { 27 | return MaterialContainerTransform() 28 | } 29 | MetaphorAnimation.FadeThrough -> { 30 | 31 | return MaterialFadeThrough() 32 | } 33 | 34 | MetaphorAnimation.Fade -> { 35 | return MaterialFade() 36 | } 37 | MetaphorAnimation.SharedAxisXForward -> { 38 | 39 | return MaterialSharedAxis(MaterialSharedAxis.X, true) 40 | } 41 | 42 | MetaphorAnimation.SharedAxisYForward -> { 43 | 44 | return MaterialSharedAxis(MaterialSharedAxis.Y, true) 45 | } 46 | 47 | MetaphorAnimation.SharedAxisZForward -> { 48 | 49 | return MaterialSharedAxis(MaterialSharedAxis.Z, true) 50 | } 51 | MetaphorAnimation.SharedAxisXBackward -> { 52 | 53 | return MaterialSharedAxis(MaterialSharedAxis.X, false) 54 | } 55 | 56 | MetaphorAnimation.SharedAxisYBackward -> { 57 | return MaterialSharedAxis(MaterialSharedAxis.Y, false) 58 | } 59 | 60 | MetaphorAnimation.SharedAxisZBackward -> { 61 | 62 | return MaterialSharedAxis(MaterialSharedAxis.Z, false) 63 | } 64 | 65 | MetaphorAnimation.ElevationScale -> { 66 | return MaterialElevationScale(false) 67 | } 68 | MetaphorAnimation.ElevationScaleGrow -> { 69 | return MaterialElevationScale(true) 70 | } 71 | 72 | MetaphorAnimation.None -> { 73 | // trick for no animations 74 | return null 75 | } 76 | } 77 | } 78 | 79 | /** applies Metaphor form attributes to a View instance. */ 80 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 81 | @JvmSynthetic 82 | @PublishedApi 83 | internal fun getWindowMetaphorAnimation(animation: MetaphorAnimation): android.transition.Transition? { 84 | 85 | when (animation) { 86 | MetaphorAnimation.ContainerTransform -> { 87 | return com.google.android.material.transition.platform.MaterialContainerTransform() 88 | } 89 | MetaphorAnimation.FadeThrough -> { 90 | 91 | return com.google.android.material.transition.platform.MaterialFadeThrough() 92 | } 93 | 94 | MetaphorAnimation.Fade -> { 95 | return com.google.android.material.transition.platform.MaterialFade() 96 | } 97 | MetaphorAnimation.SharedAxisXForward -> { 98 | 99 | return com.google.android.material.transition.platform.MaterialSharedAxis( 100 | com.google.android.material.transition.platform.MaterialSharedAxis.X, 101 | true 102 | ) 103 | } 104 | 105 | MetaphorAnimation.SharedAxisYForward -> { 106 | 107 | return com.google.android.material.transition.platform.MaterialSharedAxis( 108 | com.google.android.material.transition.platform.MaterialSharedAxis.Y, 109 | true 110 | ) 111 | } 112 | 113 | MetaphorAnimation.SharedAxisZForward -> { 114 | 115 | return com.google.android.material.transition.platform.MaterialSharedAxis( 116 | com.google.android.material.transition.platform.MaterialSharedAxis.Z, 117 | true 118 | ) 119 | } 120 | MetaphorAnimation.SharedAxisXBackward -> { 121 | 122 | return com.google.android.material.transition.platform.MaterialSharedAxis( 123 | com.google.android.material.transition.platform.MaterialSharedAxis.X, 124 | true 125 | ) 126 | } 127 | 128 | MetaphorAnimation.SharedAxisYBackward -> { 129 | return com.google.android.material.transition.platform.MaterialSharedAxis( 130 | com.google.android.material.transition.platform.MaterialSharedAxis.Y, 131 | true 132 | ) 133 | } 134 | 135 | MetaphorAnimation.SharedAxisZBackward -> { 136 | 137 | return com.google.android.material.transition.platform.MaterialSharedAxis( 138 | com.google.android.material.transition.platform.MaterialSharedAxis.Z, 139 | true 140 | ) 141 | } 142 | 143 | MetaphorAnimation.ElevationScale -> { 144 | return com.google.android.material.transition.platform.MaterialElevationScale(false) 145 | } 146 | MetaphorAnimation.ElevationScaleGrow -> { 147 | return com.google.android.material.transition.platform.MaterialElevationScale(true) 148 | } 149 | 150 | MetaphorAnimation.None -> { 151 | // trick for no animations 152 | return null 153 | } 154 | } 155 | } 156 | 157 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 158 | @JvmSynthetic 159 | @PublishedApi 160 | internal fun getInterpolator(animation: MetaphorInterpolator): Interpolator { 161 | 162 | when (animation) { 163 | MetaphorInterpolator.Standard -> { 164 | return FastOutSlowInInterpolator() 165 | } 166 | MetaphorInterpolator.Emphasized -> { 167 | return fastOutExtraSlowIn() 168 | } 169 | MetaphorInterpolator.Decelerated -> { 170 | return LinearOutSlowInInterpolator() 171 | } 172 | MetaphorInterpolator.Accelerated -> { 173 | return FastOutLinearInInterpolator() 174 | } 175 | MetaphorInterpolator.Linear -> { 176 | return LinearInterpolator() 177 | } 178 | } 179 | } 180 | 181 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 182 | private fun fastOutExtraSlowIn(): Interpolator { 183 | return PathInterpolator(PathParser.createPathFromPathData("M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1")) 184 | } 185 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 24 | 25 | 30 | 33 | 38 | 43 | 48 | 53 | 58 | 63 | 68 | 73 | 78 | 83 | 88 | 93 | 98 | 103 | 108 | 113 | 118 | 123 | 128 | 133 | 138 | 143 | 148 | 153 | 158 | 163 | 168 | 173 | 178 | 183 | 188 | 193 | 194 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorActivity.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.view.View 5 | import androidx.activity.ComponentActivity 6 | import androidx.annotation.MainThread 7 | import androidx.appcompat.app.AppCompatActivity 8 | 9 | @DslMarker 10 | internal annotation class MetaphorActivityInlineDsl 11 | 12 | /** 13 | * Creates an instance of the [MetaphorActivity] by scope of the [MetaphorActivity.Builder] using kotlin dsl. 14 | * 15 | * @param activity A context for creating resources of the [MetaphorActivity]. 16 | * @param block A dsl scope lambda from the [MetaphorActivity.Builder]. 17 | * */ 18 | @MainThread 19 | @JvmSynthetic 20 | @MetaphorActivityInlineDsl 21 | public inline fun metaphorActivity( 22 | activity: ComponentActivity, 23 | crossinline block: MetaphorActivity.Builder.() -> Unit 24 | ): MetaphorActivity = 25 | MetaphorActivity.Builder(activity).apply(block).build() 26 | 27 | /** 28 | * MetaphorActivity implements material motion animations. 29 | * 30 | * @see [MetaphorActivity](https://github.com/AndroidPoet/Metaphor) 31 | * 32 | * @param builder A [MetaphorActivity.Builder] for creating an instance of the [MetaphorActivity]. 33 | */ 34 | public class MetaphorActivity private constructor( 35 | builder: Builder 36 | ) { 37 | /** duration of enter the animations. */ 38 | public val enterDuration: Long = builder.enterDuration 39 | 40 | /** duration of reenter the animations. */ 41 | public val reenterDuration: Long = builder.reenterDuration 42 | 43 | /** duration of exit the animations. */ 44 | public val exitDuration: Long = builder.exitDuration 45 | 46 | /** duration of return the animations. */ 47 | public val returnDuration: Long = builder.returnDuration 48 | 49 | /** Enter AnimationOverlap of Activity. */ 50 | public val enterTransitionOverlap: Boolean = builder.enterTransitionOverlap 51 | 52 | /** Return AnimationOverlap of Activity. */ 53 | public val returnTransitionOverlap: Boolean = builder.returnTransitionOverlap 54 | 55 | /** Enter Animation of fragment. */ 56 | public val enterAnimation: MetaphorAnimation = builder.enterAnimation 57 | 58 | /** Exit Animation of fragment. */ 59 | public val exitAnimation: MetaphorAnimation = builder.exitAnimation 60 | 61 | /** Reenter Animation of fragment. */ 62 | public val reenterAnimation: MetaphorAnimation = builder.reenterAnimation 63 | 64 | /** Return Animation of fragment. */ 65 | public val returnAnimation: MetaphorAnimation = builder.returnAnimation 66 | 67 | /** Motion path of on fragment animation */ 68 | public val motion: android.transition.PathMotion = builder.motion 69 | 70 | /** Fragment on which animation will apply */ 71 | public val activity: ComponentActivity = builder.activity 72 | 73 | /** start view to transform into fragment */ 74 | public val view: View? = builder.view 75 | 76 | /** transitionName to transform view to another fragment */ 77 | public val transitionName: String = builder.transitionName 78 | 79 | /** Fragment on which animation will apply */ 80 | public val startActivity: Boolean = builder.startActivity 81 | 82 | /** Builder class for [MetaphorFragment]. */ 83 | @MetaphorViewInlineDsl 84 | public class Builder(public val activity: ComponentActivity) { 85 | 86 | @set:JvmSynthetic 87 | public var enterDuration: Long = 300 88 | 89 | @set:JvmSynthetic 90 | public var reenterDuration: Long = 300 91 | 92 | @set:JvmSynthetic 93 | public var exitDuration: Long = 300 94 | 95 | @set:JvmSynthetic 96 | public var returnDuration: Long = 300 97 | 98 | @set:JvmSynthetic 99 | public var enterAnimation: MetaphorAnimation = MetaphorAnimation.None 100 | 101 | @set:JvmSynthetic 102 | public var exitAnimation: MetaphorAnimation = MetaphorAnimation.None 103 | 104 | @set:JvmSynthetic 105 | public var reenterAnimation: MetaphorAnimation = MetaphorAnimation.None 106 | 107 | @set:JvmSynthetic 108 | public var returnAnimation: MetaphorAnimation = MetaphorAnimation.None 109 | 110 | @set:JvmSynthetic 111 | public var motion: android.transition.PathMotion = android.transition.ArcMotion() 112 | 113 | @set:JvmSynthetic 114 | public var view: View? = null 115 | 116 | @set:JvmSynthetic 117 | public var transitionName: String = "" 118 | 119 | @set:JvmSynthetic 120 | public var enterTransitionOverlap: Boolean = false 121 | 122 | @set:JvmSynthetic 123 | public var returnTransitionOverlap: Boolean = false 124 | 125 | @set:JvmSynthetic 126 | public var startActivity: Boolean = false 127 | 128 | /** sets the duration of the Animation. */ 129 | public fun setEnterDuration(value: Long): Builder = apply { this.enterDuration = value } 130 | 131 | /** sets the duration of the Animation. */ 132 | public fun setExitDuration(value: Long): Builder = apply { this.exitDuration = value } 133 | 134 | /** sets the duration of the Animation. */ 135 | public fun setReenterDuration(value: Long): Builder = apply { this.reenterDuration = value } 136 | 137 | /** sets the duration of the Animation. */ 138 | public fun setReturnDuration(value: Long): Builder = apply { this.returnDuration = value } 139 | 140 | /** sets enter the animation of the Activity. */ 141 | public fun setEnterAnimation(value: MetaphorAnimation): Builder = 142 | apply { this.enterAnimation = value } 143 | 144 | /** sets the exit animation of the Activity. */ 145 | public fun setExitAnimation(value: MetaphorAnimation): Builder = 146 | apply { this.exitAnimation = value } 147 | 148 | /** sets the return animation of the Activity. */ 149 | public fun setReturnAnimation(value: MetaphorAnimation): Builder = 150 | apply { this.returnAnimation = value } 151 | 152 | /** sets the reenter animation of the Activity. */ 153 | public fun setReenterAnimation(value: MetaphorAnimation): Builder = 154 | apply { this.reenterAnimation = value } 155 | 156 | /** sets the view of the Activity. */ 157 | public fun setView(value: View): Builder = apply { this.view = value } 158 | 159 | /** sets the motion of the View. */ 160 | public fun setMotion(value: android.transition.PathMotion): Builder = 161 | apply { this.motion = value } 162 | 163 | /** sets the transition of the View. */ 164 | public fun setTransitionName(value: String): Builder = apply { this.transitionName = value } 165 | 166 | /** sets the enter overlap of the Activity. */ 167 | public fun setEnterOverlap(value: Boolean): Builder = 168 | apply { this.enterTransitionOverlap = value } 169 | 170 | /** sets the return overlap of the Activity. */ 171 | public fun setReturnOverlap(value: Boolean): Builder = 172 | apply { this.returnTransitionOverlap = value } 173 | 174 | /** sets start Activity of MetaphorActivity. */ 175 | public fun setStartActivity(value: Boolean): Builder = 176 | apply { this.startActivity = value } 177 | 178 | public fun build(): MetaphorActivity = MetaphorActivity(this) 179 | } 180 | 181 | /** starts animation. */ 182 | public fun animate() { 183 | activity.applyAnimation(this) 184 | } 185 | 186 | public abstract class Factory { 187 | 188 | /** 189 | * Creates a new instance of [MetaphorActivity]. 190 | * 191 | * @return A new created instance of the [MetaphorActivity]. 192 | */ 193 | public abstract fun create(fragment: AppCompatActivity): MetaphorActivity 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /metaphor/src/main/java/com/androidpoet/metaphor/MetaphorFragment.kt: -------------------------------------------------------------------------------- 1 | 2 | package com.androidpoet.metaphor 3 | 4 | import android.graphics.Color 5 | import android.view.View 6 | import androidx.annotation.MainThread 7 | import androidx.fragment.app.Fragment 8 | import androidx.transition.ArcMotion 9 | import androidx.transition.PathMotion 10 | 11 | @DslMarker 12 | public annotation class MetaphorFragmentInlineDsl 13 | 14 | /** 15 | * Creates an instance of the [MetaphorFragment] by scope of the [MetaphorFragment.Builder] using kotlin dsl. 16 | * 17 | * @param fragment A context for creating resources of the [MetaphorFragment]. 18 | * @param block A dsl scope lambda from the [MetaphorFragment.Builder]. 19 | * */ 20 | @MainThread 21 | @JvmSynthetic 22 | @MetaphorFragmentInlineDsl 23 | public inline fun metaphorFragment( 24 | fragment: Fragment, 25 | crossinline block: MetaphorFragment.Builder.() -> Unit 26 | ): MetaphorFragment = 27 | MetaphorFragment.Builder(fragment).apply(block).build() 28 | 29 | /** 30 | * MetaphorFragment implements material motion animations. 31 | * 32 | * @see [MetaphorFragment](https://github.com/AndroidPoet/Metaphor) 33 | * 34 | * @param builder A [MetaphorFragment.Builder] for creating an instance of the [MetaphorFragment]. 35 | */ 36 | public class MetaphorFragment private constructor( 37 | builder: Builder 38 | ) { 39 | 40 | /** duration of enter the animations. */ 41 | public val enterDuration: Long = builder.enterDuration 42 | 43 | /** duration of reenter the animations. */ 44 | public val reenterDuration: Long = builder.reenterDuration 45 | 46 | /** duration of exit the animations. */ 47 | public val exitDuration: Long = builder.exitDuration 48 | 49 | /** duration of return the animations. */ 50 | public val returnDuration: Long = builder.returnDuration 51 | 52 | /** Enter Animation of fragment. */ 53 | public val enterAnimation: MetaphorAnimation = builder.enterAnimation 54 | 55 | /** Exit Animation of fragment. */ 56 | public val exitAnimation: MetaphorAnimation = builder.exitAnimation 57 | 58 | /** Reenter Animation of fragment. */ 59 | public val reenterAnimation: MetaphorAnimation = builder.reenterAnimation 60 | 61 | /** Return Animation of fragment. */ 62 | public val returnAnimation: MetaphorAnimation = builder.returnAnimation 63 | 64 | /** Enter AnimationOverlap of fragment. */ 65 | public val enterTransitionOverlap: Boolean = builder.enterTransitionOverlap 66 | 67 | /** Return AnimationOverlap of fragment. */ 68 | public val returnTransitionOverlap: Boolean = builder.returnTransitionOverlap 69 | 70 | /** Motion path of on fragment animation */ 71 | public val motion: PathMotion = builder.motion 72 | 73 | /** Fragment on which animation will apply */ 74 | public val fragment: Fragment = builder.fragment 75 | 76 | /** start view to transform into fragment */ 77 | public val view: View? = builder.view 78 | 79 | /** transitionName to transform view to another fragment */ 80 | public val transitionName: String = builder.transitionName 81 | 82 | /** scrimColor while performing animation */ 83 | public val scrimColor: Int = builder.scrimColor 84 | 85 | /** containerColors while performing animation */ 86 | public val containerColors: Int = builder.containerColors 87 | 88 | /** Builder class for [MetaphorFragment]. */ 89 | @MetaphorViewInlineDsl 90 | public class Builder(public val fragment: Fragment) { 91 | 92 | @set:JvmSynthetic 93 | public var enterDuration: Long = 300 94 | 95 | @set:JvmSynthetic 96 | public var reenterDuration: Long = 300 97 | 98 | @set:JvmSynthetic 99 | public var exitDuration: Long = 300 100 | 101 | @set:JvmSynthetic 102 | public var returnDuration: Long = 300 103 | 104 | @set:JvmSynthetic 105 | public var enterAnimation: MetaphorAnimation = MetaphorAnimation.None 106 | 107 | @set:JvmSynthetic 108 | public var exitAnimation: MetaphorAnimation = MetaphorAnimation.None 109 | 110 | @set:JvmSynthetic 111 | public var reenterAnimation: MetaphorAnimation = MetaphorAnimation.None 112 | 113 | @set:JvmSynthetic 114 | public var returnAnimation: MetaphorAnimation = MetaphorAnimation.None 115 | 116 | @set:JvmSynthetic 117 | public var enterTransitionOverlap: Boolean = false 118 | 119 | @set:JvmSynthetic 120 | public var returnTransitionOverlap: Boolean = false 121 | 122 | @set:JvmSynthetic 123 | public var motion: PathMotion = ArcMotion() 124 | 125 | @set:JvmSynthetic 126 | public var view: View? = null 127 | 128 | @set:JvmSynthetic 129 | public var transitionName: String = "" 130 | 131 | @set:JvmSynthetic 132 | public var scrimColor: Int = Color.TRANSPARENT 133 | 134 | @set:JvmSynthetic 135 | public var containerColors: Int = Color.TRANSPARENT 136 | 137 | /** sets the duration of the animation. */ 138 | public fun setEnterDuration(value: Long): Builder = apply { this.enterDuration = value } 139 | 140 | /** sets the duration of the animation. */ 141 | public fun setExitDuration(value: Long): Builder = apply { this.exitDuration = value } 142 | 143 | /** sets the duration of the animation. */ 144 | public fun setReenterDuration(value: Long): Builder = apply { this.reenterDuration = value } 145 | 146 | /** sets the duration of the animation. */ 147 | public fun setReturnDuration(value: Long): Builder = apply { this.returnDuration = value } 148 | 149 | /** sets enter the animation of the Fragment. */ 150 | public fun setEnterAnimation(value: MetaphorAnimation): Builder = 151 | apply { this.enterAnimation = value } 152 | 153 | /** sets the exit animation of the Fragment. */ 154 | public fun setExitAnimation(value: MetaphorAnimation): Builder = 155 | apply { this.exitAnimation = value } 156 | 157 | /** sets the return animation of the Fragment. */ 158 | public fun setReturnAnimation(value: MetaphorAnimation): Builder = 159 | apply { this.returnAnimation = value } 160 | 161 | /** sets the reenter animation of the Fragment. */ 162 | public fun setReenterAnimation(value: MetaphorAnimation): Builder = 163 | apply { this.reenterAnimation = value } 164 | 165 | /** sets the SetView of the Fragment. */ 166 | public fun setView(value: View): Builder = 167 | apply { this.view = value } 168 | 169 | /** sets the Motion of the animation. */ 170 | public fun setMotion(value: PathMotion): Builder = 171 | apply { this.motion = value } 172 | 173 | /** sets the TransitionName of the View. */ 174 | public fun setTransitionName(value: String): Builder = 175 | apply { this.transitionName = value } 176 | 177 | /** sets the enter Overlap of the Fragment. */ 178 | public fun setEnterOverlap(value: Boolean): Builder = 179 | apply { this.enterTransitionOverlap = value } 180 | 181 | /** sets the return Overlap of the Fragment. */ 182 | public fun setReturnOverlap(value: Boolean): Builder = 183 | apply { this.returnTransitionOverlap = value } 184 | 185 | /** sets the ScrimColor of the Fragment. */ 186 | public fun setScrimColor(value: Int): Builder = 187 | apply { this.scrimColor = value } 188 | 189 | /** sets the ScrimColor of the Fragment. */ 190 | public fun setContainerColor(value: Int): Builder = 191 | apply { this.containerColors = value } 192 | 193 | public fun build(): MetaphorFragment = MetaphorFragment(this) 194 | } 195 | 196 | /** starts animation. */ 197 | public fun animate() { 198 | fragment.applyAnimation(this) 199 | } 200 | 201 | public abstract class Factory { 202 | 203 | /** 204 | * Creates a new instance of [MetaphorFragment]. 205 | * 206 | * @return A new created instance of the [MetaphorFragment]. 207 | */ 208 | public abstract fun create(fragment: Fragment): MetaphorFragment 209 | } 210 | } 211 | --------------------------------------------------------------------------------