├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bitvale │ │ └── droidmotion │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── bitvale │ │ │ └── droidmotion │ │ │ ├── MainActivity.kt │ │ │ ├── adapter │ │ │ ├── BaseViewHolder.kt │ │ │ └── RecyclerAdapter.kt │ │ │ ├── common │ │ │ ├── Constants.kt │ │ │ └── Extensions.kt │ │ │ ├── fragment │ │ │ ├── BaseFragment.kt │ │ │ ├── DetailsFragment.kt │ │ │ └── RecyclerFragment.kt │ │ │ ├── listener │ │ │ ├── BottomNavigationViewListener.kt │ │ │ └── OnBackPressedListener.kt │ │ │ └── model │ │ │ └── DataProvider.kt │ └── res │ │ ├── anim │ │ ├── item_animation_fall_down.xml │ │ └── layout_animation_fall_down.xml │ │ ├── animator │ │ └── main_list_animator.xml │ │ ├── drawable-hdpi │ │ └── splash.png │ │ ├── drawable-mdpi │ │ └── splash.png │ │ ├── drawable-xhdpi │ │ └── splash.png │ │ ├── drawable-xxhdpi │ │ └── splash.png │ │ ├── drawable-xxxhdpi │ │ └── splash.png │ │ ├── drawable │ │ ├── bottom_navigation_selector.xml │ │ ├── ic_complaint.xml │ │ ├── ic_correct.xml │ │ ├── ic_error.xml │ │ ├── ic_export.xml │ │ ├── ic_file.xml │ │ ├── ic_incorrect.xml │ │ ├── ic_left.xml │ │ ├── ic_menu_account.xml │ │ ├── ic_menu_dialog.xml │ │ ├── ic_menu_gift.xml │ │ ├── ic_menu_main.xml │ │ ├── ic_menu_wallet.xml │ │ ├── ic_negative.xml │ │ ├── ic_not_received.xml │ │ ├── ic_positive.xml │ │ ├── ic_received.xml │ │ ├── ic_right.xml │ │ ├── ic_sort.xml │ │ ├── img_apple.xml │ │ ├── img_dribble.xml │ │ ├── img_facebook.xml │ │ ├── img_github.xml │ │ ├── img_google_play.xml │ │ ├── img_instagram.xml │ │ ├── img_twitter.xml │ │ ├── img_vimeo.xml │ │ ├── img_vk.xml │ │ ├── img_youtube.xml │ │ └── splash_screen.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_details.xml │ │ ├── fragment_recycler.xml │ │ ├── item_card.xml │ │ └── item_details.xml │ │ ├── menu │ │ └── bottom_navigation_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── transition │ │ └── shared_element_transition.xml │ │ ├── values-sw600dp │ │ └── dimens.xml │ │ ├── values-sw720dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── bitvale │ └── droidmotion │ └── ExampleUnitTest.kt ├── art ├── dribbble.png ├── google_play.png ├── medium.png └── preview.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | .idea 13 | app/release/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Alexander Kolpakov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DroidMotion 2 | 3 | sample 4 | 5 | [![Platform](https://img.shields.io/badge/platform-android-green.svg)](http://developer.android.com/index.html) 6 | [![API](https://img.shields.io/badge/API-19%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=19) 7 | [![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](/LICENSE) 8 | 9 | This is an Android sample project that shows how to implement a simple android motion. 10 | 11 | # 12 | 13 | You can see the description of the implementation on medium and get the sample app with similar motions on Google Play. 14 | 15 | 16 | Android app on Google Play 17 | 18 | 19 | 20 | Read on Medium 21 | 22 | 23 | 24 | Design on Dribbble 25 | 26 | 27 | # -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion rootProject.compileSdkVersion 9 | defaultConfig { 10 | applicationId "com.bitvale.droidmotion" 11 | minSdkVersion rootProject.minSdkVersion 12 | targetSdkVersion rootProject.targetSdkVersion 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | 17 | vectorDrawables.useSupportLibrary = true 18 | } 19 | buildTypes { 20 | debug { 21 | applicationIdSuffix '.debug' 22 | versionNameSuffix '-DEBUG' 23 | useProguard false 24 | minifyEnabled false 25 | } 26 | release { 27 | minifyEnabled true 28 | useProguard true 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | androidExtensions { 33 | experimental = true 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(dir: 'libs', include: ['*.jar']) 39 | // Testing dependencies 40 | testImplementation "junit:junit:$rootProject.junitVersion" 41 | 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$rootProject.kotlinVersion" 43 | implementation "androidx.appcompat:appcompat:$rootProject.androidxVersion" 44 | implementation "androidx.recyclerview:recyclerview:$rootProject.androidxVersion" 45 | implementation "androidx.cardview:cardview:$rootProject.androidxVersion" 46 | implementation "com.google.android.material:material:$rootProject.materialVersion" 47 | implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" 48 | implementation "androidx.core:core-ktx:$rootProject.androidxVersion" 49 | } 50 | -------------------------------------------------------------------------------- /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 | # hideBottomNavigationView the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bitvale/droidmotion/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.bitvale.animation", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.fragment.app.Fragment 6 | import com.bitvale.androidmotion.R 7 | import com.bitvale.droidmotion.common.replaceFragmentInActivity 8 | import com.bitvale.droidmotion.fragment.RecyclerFragment 9 | import com.bitvale.droidmotion.listener.BottomNavigationViewListener 10 | import com.bitvale.droidmotion.listener.OnBackPressedListener 11 | import kotlinx.android.synthetic.main.activity_main.* 12 | 13 | /** 14 | * Created by Alexander Kolpakov on 25.07.2018 15 | */ 16 | class MainActivity : AppCompatActivity(), BottomNavigationViewListener { 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | setTheme(R.style.AppTheme) 20 | super.onCreate(savedInstanceState) 21 | setContentView(R.layout.activity_main) 22 | setupFragment(RecyclerFragment.newInstance()) 23 | } 24 | 25 | private fun setupFragment(fragment: Fragment) { 26 | supportFragmentManager.findFragmentById(R.id.fragment_container) 27 | ?: fragment.let { 28 | replaceFragmentInActivity(it, R.id.fragment_container) 29 | } 30 | } 31 | 32 | override fun hideBottomNavigationView() { 33 | if (bottom_navigation.translationY == 0f) 34 | bottom_navigation.animate() 35 | .translationY(bottom_navigation.height.toFloat()) 36 | .setDuration(250) 37 | .start() 38 | } 39 | 40 | override fun showBottomNavigationView() { 41 | if (bottom_navigation.translationY >= bottom_navigation.height.toFloat()) 42 | bottom_navigation.animate() 43 | .translationY(0f) 44 | .setDuration(400) 45 | .start() 46 | } 47 | 48 | override fun onBackPressed() { 49 | val fragmentList = supportFragmentManager.fragments 50 | var proceedToSuper = true 51 | for (fragment in fragmentList) { 52 | if (fragment is OnBackPressedListener) { 53 | proceedToSuper = false 54 | (fragment as OnBackPressedListener).onBackPressed() 55 | } 56 | } 57 | if (proceedToSuper) super.onBackPressed() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/adapter/BaseViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.adapter 2 | 3 | import android.os.Build 4 | import androidx.recyclerview.widget.RecyclerView 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.bitvale.androidmotion.R 8 | import com.bitvale.droidmotion.common.inflate 9 | import com.bitvale.droidmotion.model.DataProvider.BaseData 10 | import com.bitvale.droidmotion.model.DataProvider.Card 11 | import com.bitvale.droidmotion.model.DataProvider.Details 12 | import com.bitvale.droidmotion.common.TRANSITION_CARD 13 | import kotlinx.android.extensions.LayoutContainer 14 | import kotlinx.android.synthetic.main.item_card.* 15 | import kotlinx.android.synthetic.main.item_details.* 16 | 17 | /** 18 | * Created by Alexander Kolpakov on 29.07.2018 19 | */ 20 | abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer { 21 | 22 | override val containerView: View? 23 | get() = itemView 24 | 25 | abstract fun bind(data: T, listener: View.OnClickListener? = null) 26 | 27 | 28 | class CardViewHolder(parent: ViewGroup) : BaseViewHolder(parent.inflate(R.layout.item_card)) { 29 | 30 | override fun bind(data: Card, listener: View.OnClickListener?) { 31 | containerView?.setOnClickListener(listener) 32 | 33 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 34 | containerView?.transitionName = TRANSITION_CARD + adapterPosition 35 | } 36 | 37 | tv_title.text = data.name 38 | tv_amount.text = data.amount 39 | tv_date.text = data.date 40 | tv_status.text = data.status.code 41 | img_status.setImageResource(data.status.iconId) 42 | img_card.setImageResource(data.imageId) 43 | } 44 | } 45 | 46 | class DetailsViewHolder(parent: ViewGroup) : BaseViewHolder
(parent.inflate(R.layout.item_details)) { 47 | 48 | override fun bind(data: Details, listener: View.OnClickListener?) { 49 | tv_details_title.text = data.title 50 | tv_details_subtitle.text = data.subtitle 51 | tv_details_amount.text = data.amount 52 | } 53 | 54 | } 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/adapter/RecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.adapter 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import com.bitvale.droidmotion.model.DataProvider.BaseData 7 | import com.bitvale.droidmotion.model.DataProvider.Details 8 | import com.bitvale.droidmotion.adapter.BaseViewHolder.DetailsViewHolder 9 | import com.bitvale.droidmotion.adapter.BaseViewHolder.CardViewHolder 10 | 11 | 12 | /** 13 | * Created by Alexander Kolpakov on 17.07.2018 14 | */ 15 | class RecyclerAdapter(private var dataSet: List, private var listener: View.OnClickListener? = null) 16 | : RecyclerView.Adapter>() { 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { 20 | return when (viewType) { 21 | 0 -> DetailsViewHolder(parent) as BaseViewHolder 22 | else -> CardViewHolder(parent) as BaseViewHolder 23 | } 24 | } 25 | 26 | override fun getItemCount() = dataSet.size 27 | 28 | override fun getItemViewType(position: Int): Int { 29 | val type = dataSet[0].javaClass 30 | return when (type) { 31 | Details::class.java -> 0 32 | else -> 1 33 | } 34 | } 35 | 36 | override fun onBindViewHolder(holder: BaseViewHolder, position: Int) = holder.bind(dataSet[position], listener) 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/common/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.common 2 | 3 | 4 | const val TRANSITION_CARD = "card_transition_" 5 | const val TRANSITION_TOOLBAR = "toolbar_transition" 6 | 7 | const val EXTRA_COORDINATES = "coordinates" 8 | const val EXTRA_POSITION = "position" -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/common/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.common 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.content.Context 6 | import android.os.Build 7 | import android.util.TypedValue 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.ImageView 12 | import androidx.annotation.LayoutRes 13 | import androidx.appcompat.app.AppCompatActivity 14 | import androidx.cardview.widget.CardView 15 | import androidx.core.view.drawToBitmap 16 | import androidx.fragment.app.Fragment 17 | import androidx.fragment.app.FragmentManager 18 | import androidx.fragment.app.FragmentTransaction 19 | import com.bitvale.androidmotion.R 20 | 21 | /** 22 | * Created by Alexander Kolpakov on 17.07.2018 23 | */ 24 | fun ViewGroup.inflate(@LayoutRes layoutId: Int, attachToRoot: Boolean = false): View { 25 | return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) 26 | } 27 | 28 | fun Context.getStatusBarHeight(): Int { 29 | val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") 30 | return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) 31 | else 0 32 | } 33 | 34 | fun Context.getToolbarHeight(): Int { 35 | val tv = TypedValue() 36 | return if (theme.resolveAttribute(android.R.attr.actionBarSize, tv, true)) 37 | TypedValue.complexToDimensionPixelSize(tv.data, resources.displayMetrics) 38 | else 0 39 | } 40 | 41 | fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, frameId: Int) { 42 | supportFragmentManager.transact { 43 | replace(frameId, fragment) 44 | } 45 | } 46 | 47 | private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) { 48 | beginTransaction().apply { 49 | action() 50 | }.commit() 51 | } 52 | 53 | fun View.copyViewImage(): View { 54 | val copy = ImageView(context) 55 | 56 | val bitmap = drawToBitmap() 57 | copy.setImageBitmap(bitmap) 58 | 59 | // On pre-Lollipop when we create a copy, the card view's shadow is copied too as content and 60 | // we do not need an additional card view. 61 | 62 | return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 63 | CardView(context).apply { 64 | cardElevation = resources.getDimension(R.dimen.card_elevation) 65 | radius = resources.getDimension(R.dimen.card_corner_radius) 66 | addView(copy) 67 | } 68 | } else { 69 | copy 70 | }).apply { 71 | layoutParams = this@copyViewImage.layoutParams 72 | layoutParams.height = this@copyViewImage.height 73 | layoutParams.width = this@copyViewImage.width 74 | x = this@copyViewImage.x 75 | y = this@copyViewImage.y 76 | } 77 | } 78 | 79 | fun Animator.withEndAction(action: () -> Unit): Animator { 80 | addListener(object : AnimatorListenerAdapter() { 81 | override fun onAnimationEnd(animation: Animator?) { 82 | action() 83 | } 84 | }) 85 | return this 86 | } 87 | 88 | fun Animator.withStartAction(action: () -> Unit): Animator { 89 | addListener(object : AnimatorListenerAdapter() { 90 | override fun onAnimationStart(animation: Animator?) { 91 | action() 92 | } 93 | }) 94 | return this 95 | } 96 | 97 | inline fun supportsLollipop(action: () -> Unit) { 98 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 99 | action() 100 | } 101 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/fragment/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.cardview.widget.CardView 7 | import androidx.core.view.updatePadding 8 | import androidx.fragment.app.Fragment 9 | import com.bitvale.androidmotion.R 10 | import com.bitvale.droidmotion.common.getStatusBarHeight 11 | 12 | /** 13 | * Created by Alexander Kolpakov on 26.07.2018 14 | */ 15 | open class BaseFragment : Fragment() { 16 | 17 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 18 | setupToolbarMargin(view) 19 | } 20 | 21 | private fun setupToolbarMargin(view: View) { 22 | val toolbar = view.findViewById(R.id.toolbar) 23 | toolbar?.let { 24 | val statusBarHeight = context?.getStatusBarHeight() 25 | val lp = it.layoutParams as ViewGroup.LayoutParams 26 | lp.height += statusBarHeight as Int 27 | if (it is CardView) { 28 | it.setContentPadding(0, statusBarHeight, 0, 0) 29 | } else { 30 | it.updatePadding(top = statusBarHeight) 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/fragment/DetailsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.fragment 2 | 3 | import android.animation.AnimatorInflater 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.view.animation.AnticipateInterpolator 9 | import android.view.animation.OvershootInterpolator 10 | import androidx.core.view.doOnLayout 11 | import androidx.core.view.updatePadding 12 | import com.bitvale.androidmotion.R 13 | import com.bitvale.droidmotion.common.* 14 | import com.bitvale.droidmotion.listener.OnBackPressedListener 15 | import com.bitvale.droidmotion.model.DataProvider 16 | import kotlinx.android.synthetic.main.fragment_details.* 17 | import kotlinx.android.synthetic.main.item_card.* 18 | 19 | 20 | /** 21 | * Created by Alexander Kolpakov on 25.07.2018 22 | */ 23 | class DetailsFragment : BaseFragment(), OnBackPressedListener { 24 | 25 | private lateinit var coordinates: FloatArray 26 | 27 | companion object { 28 | const val TAG = "DetailsFragment" 29 | 30 | fun newInstance(coordinates: FloatArray, adapterPosition: Int): DetailsFragment { 31 | 32 | val bundle = Bundle().apply { 33 | putFloatArray(EXTRA_COORDINATES, coordinates) 34 | putInt(EXTRA_POSITION, adapterPosition) 35 | } 36 | 37 | return DetailsFragment().apply { arguments = bundle } 38 | } 39 | } 40 | 41 | override fun onCreate(savedInstanceState: Bundle?) { 42 | super.onCreate(savedInstanceState) 43 | arguments?.let { 44 | coordinates = it.getFloatArray(EXTRA_COORDINATES) 45 | } 46 | } 47 | 48 | override fun onCreateView( 49 | inflater: LayoutInflater, 50 | container: ViewGroup?, 51 | savedInstanceState: Bundle? 52 | ): View? = inflater.inflate(R.layout.fragment_details, container, false) 53 | 54 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 55 | super.onViewCreated(view, savedInstanceState) 56 | 57 | val position = if (arguments != null) (arguments as Bundle).getInt(EXTRA_POSITION) 58 | else 0 59 | 60 | setupViews(position) 61 | 62 | if (savedInstanceState == null) { 63 | animateToolbar() 64 | fab_negative.animate() 65 | .translationY(0f) 66 | .setDuration(650) 67 | .setInterpolator(OvershootInterpolator(4f)) 68 | .start() 69 | fab_positive.animate() 70 | .translationY(0f) 71 | .setStartDelay(100) 72 | .setDuration(650) 73 | .setInterpolator(OvershootInterpolator(4f)) 74 | .start() 75 | } else { 76 | toolbar.alpha = 1f 77 | } 78 | } 79 | 80 | private fun setupViews(position: Int) { 81 | supportsLollipop { 82 | details_card.transitionName = TRANSITION_CARD + position 83 | toolbar_container.transitionName = TRANSITION_TOOLBAR 84 | } 85 | 86 | (details_card.layoutParams as ViewGroup.MarginLayoutParams).topMargin = coordinates[2].toInt() 87 | 88 | val data = DataProvider.getCardData()[position] 89 | tv_title.text = data.name 90 | tv_amount.text = data.amount 91 | tv_date.text = data.date 92 | tv_status.text = data.status.code 93 | img_status.setImageResource(data.status.iconId) 94 | img_card.setImageResource(data.imageId) 95 | 96 | fab_negative.setOnClickListener { onBackPressed() } 97 | 98 | with(recycler_view) { 99 | adapter = com.bitvale.droidmotion.adapter.RecyclerAdapter(DataProvider.getDetailsData()) 100 | setHasFixedSize(true) 101 | fab_negative.doOnLayout { 102 | val paddingBottom = (paddingBottom + fab_negative.height * 1.5).toInt() 103 | updatePadding(bottom = paddingBottom) 104 | } 105 | } 106 | } 107 | 108 | override fun onBackPressed() { 109 | animateViewsOut() 110 | } 111 | 112 | private fun animateViewsOut() { 113 | val translateTo = fab_negative.height * 2f 114 | AnimatorInflater.loadAnimator(activity, R.animator.main_list_animator).apply { 115 | setTarget(recycler_view) 116 | start() 117 | } 118 | 119 | fab_negative.animate() 120 | .translationY(translateTo) 121 | .setDuration(350) 122 | .setInterpolator(AnticipateInterpolator(2f)) 123 | .start() 124 | fab_positive.animate() 125 | .translationY(translateTo) 126 | .setStartDelay(50) 127 | .setDuration(350) 128 | .withEndAction { 129 | activity?.supportFragmentManager?.popBackStack() 130 | } 131 | .setInterpolator(AnticipateInterpolator(2f)) 132 | .start() 133 | 134 | animateToolbar(0f, 350) 135 | } 136 | 137 | private fun animateToolbar(alphaTo: Float = 1f, duration: Long = 200) { 138 | toolbar.animate().alpha(alphaTo).setDuration(duration).start() 139 | } 140 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/fragment/RecyclerFragment.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.fragment 2 | 3 | import android.animation.AnimatorInflater 4 | import android.animation.ValueAnimator 5 | import android.os.Bundle 6 | import android.transition.TransitionInflater 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import androidx.core.view.doOnLayout 11 | import androidx.fragment.app.FragmentTransaction 12 | import androidx.recyclerview.widget.LinearLayoutManager 13 | import androidx.recyclerview.widget.RecyclerView 14 | import com.bitvale.androidmotion.R 15 | import com.bitvale.droidmotion.adapter.RecyclerAdapter 16 | import com.bitvale.droidmotion.common.* 17 | import com.bitvale.droidmotion.listener.BottomNavigationViewListener 18 | import com.bitvale.droidmotion.model.DataProvider 19 | import kotlinx.android.synthetic.main.fragment_recycler.* 20 | 21 | 22 | /** 23 | * Created by Alexander Kolpakov on 25.07.2018 24 | */ 25 | class RecyclerFragment : BaseFragment(), View.OnClickListener { 26 | 27 | companion object { 28 | const val TAG = "RecyclerFragment" 29 | 30 | fun newInstance(): RecyclerFragment { 31 | return RecyclerFragment() 32 | } 33 | } 34 | 35 | var bottomNavListener: BottomNavigationViewListener? = null 36 | 37 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 38 | return inflater.inflate(R.layout.fragment_recycler, container, false) 39 | } 40 | 41 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 42 | super.onViewCreated(view, savedInstanceState) 43 | 44 | setupViews() 45 | 46 | if (savedInstanceState == null) { 47 | root.doOnLayout { 48 | toolbar.animate() 49 | .translationY(0f) 50 | .alpha(1f) 51 | .setDuration(400) 52 | .start() 53 | bottomNavListener?.showBottomNavigationView() 54 | } 55 | } else { 56 | toolbar.alpha = 1f 57 | toolbar.translationY = 0f 58 | } 59 | } 60 | 61 | private fun setupViews() { 62 | supportsLollipop { 63 | details_toolbar_transition_helper.transitionName = TRANSITION_TOOLBAR 64 | } 65 | 66 | details_toolbar_transition_helper.translationY = -resources.getDimension(R.dimen.details_toolbar_container_height) 67 | toolbar.translationY = -toolbar.context.getToolbarHeight().toFloat() 68 | 69 | val elevation = resources.getDimension(R.dimen.toolbar_elevation) 70 | 71 | with(recycler_view) { 72 | adapter = RecyclerAdapter(DataProvider.getCardData(), this@RecyclerFragment) 73 | setHasFixedSize(true) 74 | val lm = layoutManager as LinearLayoutManager 75 | addOnScrollListener(object : RecyclerView.OnScrollListener() { 76 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 77 | if (dy > 0) bottomNavListener?.hideBottomNavigationView() 78 | if (dy < 0) bottomNavListener?.showBottomNavigationView() 79 | 80 | if (lm.findFirstCompletelyVisibleItemPosition() == 0) { 81 | if (toolbar.cardElevation == 0f) return 82 | animateToolbarElevation(true) 83 | 84 | } else { 85 | if (toolbar.cardElevation > 0f) return 86 | toolbar.cardElevation = elevation 87 | } 88 | } 89 | }) 90 | } 91 | } 92 | 93 | override fun onActivityCreated(savedInstanceState: Bundle?) { 94 | super.onActivityCreated(savedInstanceState) 95 | if (activity is BottomNavigationViewListener) { 96 | bottomNavListener = activity as BottomNavigationViewListener 97 | } else { 98 | throw ClassCastException("$activity must implement BottomNavigationViewListener") 99 | } 100 | } 101 | 102 | override fun onClick(view: View) { 103 | val fragmentTransaction = initFragmentTransaction(view) 104 | val copy = view.copyViewImage() 105 | copy.y += toolbar.height 106 | root.addView(copy) 107 | view.visibility = View.INVISIBLE 108 | startAnimation(copy, fragmentTransaction) 109 | } 110 | 111 | private fun initFragmentTransaction(view: View): FragmentTransaction? { 112 | val toY = view.resources.getDimensionPixelOffset(R.dimen.details_toolbar_container_height) - view.height / 2f 113 | 114 | val positions = FloatArray(3) 115 | positions[0] = view.x 116 | positions[1] = view.y + toolbar.height 117 | positions[2] = toY 118 | 119 | val adapterPosition = recycler_view.getChildAdapterPosition(view) 120 | val detailsFragment = DetailsFragment.newInstance(positions, adapterPosition) 121 | 122 | val transaction = fragmentManager?.beginTransaction() 123 | ?.replace(R.id.fragment_container, detailsFragment, DetailsFragment.TAG) 124 | ?.addToBackStack(null) 125 | 126 | supportsLollipop { 127 | val transition = TransitionInflater.from(context) 128 | .inflateTransition(R.transition.shared_element_transition) 129 | detailsFragment.sharedElementEnterTransition = transition 130 | 131 | transaction 132 | ?.addSharedElement(view, view.transitionName) 133 | ?.addSharedElement(details_toolbar_transition_helper, details_toolbar_transition_helper.transitionName) 134 | } 135 | 136 | return transaction 137 | } 138 | 139 | private fun startAnimation(view: View, fragmentTransaction: FragmentTransaction?) { 140 | AnimatorInflater.loadAnimator(activity, R.animator.main_list_animator).apply { 141 | setTarget(recycler_view) 142 | withStartAction { if (toolbar.cardElevation > 0) animateToolbarElevation(true) } 143 | withEndAction { 144 | recycler_view.visibility = View.INVISIBLE 145 | 146 | val toY = view.resources.getDimensionPixelOffset(R.dimen.details_toolbar_container_height) - view.height / 2f 147 | 148 | view.animate().y(toY).start() 149 | 150 | toolbar.animate() 151 | .translationY(-toolbar.height.toFloat()) 152 | .alpha(0f) 153 | .setDuration(600) 154 | .withStartAction { 155 | bottomNavListener?.hideBottomNavigationView() 156 | details_toolbar_transition_helper.animate().translationY(0f).setDuration(500).start() 157 | } 158 | .withEndAction { 159 | fragmentTransaction?.commitAllowingStateLoss() 160 | } 161 | .start() 162 | } 163 | start() 164 | } 165 | } 166 | 167 | private fun animateToolbarElevation(animateOut: Boolean) { 168 | var valueFrom = resources.getDimension(R.dimen.toolbar_elevation) 169 | var valueTo = 0f 170 | if (!animateOut) { 171 | valueTo = valueFrom 172 | valueFrom = 0f 173 | } 174 | ValueAnimator.ofFloat(valueFrom, valueTo).setDuration(250).apply { 175 | startDelay = 0 176 | addUpdateListener { toolbar.cardElevation = it.animatedValue as Float } 177 | start() 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/listener/BottomNavigationViewListener.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.listener 2 | 3 | /** 4 | * Created by Alexander Kolpakov on 29.07.2018 5 | */ 6 | interface BottomNavigationViewListener { 7 | fun hideBottomNavigationView() 8 | fun showBottomNavigationView() 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/listener/OnBackPressedListener.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.listener 2 | 3 | /** 4 | * Created by Alexander Kolpakov on 26.07.2018 5 | */ 6 | interface OnBackPressedListener { 7 | fun onBackPressed() 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/bitvale/droidmotion/model/DataProvider.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion.model 2 | 3 | import androidx.annotation.DrawableRes 4 | import com.bitvale.androidmotion.R 5 | 6 | /** 7 | * Created by Alexander Kolpakov on 24.07.2018 8 | */ 9 | object DataProvider { 10 | 11 | fun getCardData(): List { 12 | val data = ArrayList() 13 | data.add(Card(0, "Google Play", "28.07.2018", "$38,456.78", Status.COMPLAINT, R.drawable.img_google_play)) 14 | data.add(Card(1, "Twitter", "27.07.2018", "$1,550.60", Status.RECEIVED, R.drawable.img_twitter)) 15 | data.add(Card(2, "YouTube", "27.07.2018", "$14,340.00", Status.CORRECT, R.drawable.img_youtube)) 16 | data.add(Card(3, "Dribbble", "26.07.2018", "$2,678.27", Status.ERROR, R.drawable.img_dribble)) 17 | data.add(Card(4, "Apple Store", "26.07.2018", "$20,479.12", Status.CORRECT, R.drawable.img_apple)) 18 | data.add(Card(5, "VK", "25.07.2018", "$13,846.13", Status.INCORRECT, R.drawable.img_vk)) 19 | data.add(Card(6, "Instagram", "25.07.2018", "$24,856.17", Status.NOT_RECEIVED, R.drawable.img_instagram)) 20 | data.add(Card(7, "Github", "24.07.2018", "$376.90", Status.COMPLAINT, R.drawable.img_github)) 21 | data.add(Card(8, "Vimeo", "23.07.2018", "$7,568.02", Status.RECEIVED, R.drawable.img_vimeo)) 22 | data.add(Card(9, "Facebook", "10.07.2018", "$18,347.32", Status.INCORRECT, R.drawable.img_facebook)) 23 | return data 24 | } 25 | 26 | fun getDetailsData(): List
{ 27 | val data = ArrayList
() 28 | data.add(Details(0, "In App purchase\"Gem\"", "$14,340.00 X1 (including VAT 10%)", "$14,340.00")) 29 | data.add(Details(0, "In App purchase \"Money\"", "$2,456.78 X1 (including VAT 10%)", "$2,456.78")) 30 | data.add(Details(0, "Interstitial Ads", "$1,150.15 X1 (including VAT 10%)", "$1,150.15")) 31 | data.add(Details(0, "Rewarded video", "$566.20 X1 (including VAT 10%)", "$566.20")) 32 | return data 33 | } 34 | 35 | interface BaseData 36 | 37 | data class Card(val id: Int, val name: String, val date: String, val amount: String, val status: Status, 38 | @DrawableRes val imageId: Int) : BaseData 39 | 40 | data class Details(val id: Int, val title: String, val subtitle: String, val amount: String) : BaseData 41 | 42 | enum class Status(val code: String, @DrawableRes val iconId: Int) { 43 | CORRECT("Correct", R.drawable.ic_correct), 44 | INCORRECT("Incorrect", R.drawable.ic_incorrect), 45 | COMPLAINT("Complaint", R.drawable.ic_complaint), 46 | ERROR("Error", R.drawable.ic_error), 47 | RECEIVED("Received", R.drawable.ic_received), 48 | NOT_RECEIVED("Not received", R.drawable.ic_not_received) 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/item_animation_fall_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/anim/layout_animation_fall_down.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/animator/main_list_animator.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_navigation_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_complaint.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_correct.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_export.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_file.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_incorrect.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_account.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_gift.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | 19 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_wallet.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_not_received.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_received.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_apple.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_dribble.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_facebook.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_github.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_google_play.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_instagram.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_twitter.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_vimeo.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_vk.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_youtube.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 28 | 29 | 40 | 41 | 53 | 54 | 66 | 67 | 68 | 69 | 70 | 71 | 82 | 83 | 100 | 101 | 116 | 117 | 132 | 133 | 139 | 140 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recycler.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 21 | 22 | 25 | 26 | 37 | 38 | 47 | 48 | 57 | 58 | 59 | 60 | 61 | 70 | 71 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_card.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 19 | 20 | 27 | 28 | 38 | 39 | 48 | 49 | 58 | 59 | 68 | 69 | 80 | 81 | 89 | 90 | 98 | 99 | 108 | 109 | 115 | 116 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 23 | 24 | 35 | 36 | 45 | 46 | 55 | 56 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_navigation_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 10 | 16 | 22 | 23 | 29 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/transition/shared_element_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 164dp 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw720dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8dp 5 | 12dp 6 | 20dp 7 | 36dp 8 | 52dp 9 | 68dp 10 | 11 | 28sp 12 | 24sp 13 | 20sp 14 | 18sp 15 | 16sp 16 | 14sp 17 | 76dp 18 | 19 | 196dp 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3aa3f7 4 | #025497 5 | #008dff 6 | 7 | #ff4d6a 8 | @color/colorAccent 9 | 10 | @color/colorAccent 11 | 12 | #505a595b 13 | 14 | #4d4d4d 15 | #eee 16 | @android:color/white 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26sp 4 | 22sp 5 | 18sp 6 | 16sp 7 | 14sp 8 | 12sp 9 | 10 | 4dp 11 | 8dp 12 | 16dp 13 | 24dp 14 | 32dp 15 | 48dp 16 | 64dp 17 | 18 | @dimen/space_tiny 19 | @dimen/space_small 20 | @dimen/space_normal 21 | @dimen/space_large 22 | @dimen/space_medium 23 | 24 | @dimen/space_tiny 25 | @dimen/space_small 26 | @dimen/space_normal 27 | @dimen/space_medium 28 | @dimen/space_large 29 | 30 | @dimen/space_xlarge 31 | 100dp 32 | @dimen/space_tiny 33 | 34 | @dimen/space_tiny 35 | @dimen/space_tiny 36 | @dimen/space_small 37 | 38 | 56dp 39 | @dimen/space_medium 40 | 41 | 156dp 42 | 146dp 43 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | DroidMotion 4 | My income 5 | Status 6 | Date 7 | Amount 8 | Back 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 14 | 15 | 16 | 21 | 22 | 26 | 27 | 30 | 31 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/test/java/com/bitvale/droidmotion/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.bitvale.droidmotion 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /art/dribbble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/art/dribbble.png -------------------------------------------------------------------------------- /art/google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/art/google_play.png -------------------------------------------------------------------------------- /art/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/art/medium.png -------------------------------------------------------------------------------- /art/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/art/preview.gif -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | ext { 6 | // Sdk and tools 7 | compileSdkVersion = 28 8 | minSdkVersion = 19 9 | targetSdkVersion = 28 10 | 11 | // App dependencies 12 | constraintLayoutVersion = '1.1.2' 13 | gradleVersion = '3.3.0-alpha10' 14 | junitVersion = '4.12' 15 | kotlinVersion = '1.2.61' 16 | androidxVersion = "1.0.0-rc02" 17 | materialVersion = "1.0.0-rc01" 18 | constraintLayoutVersion = '1.1.2' 19 | } 20 | 21 | repositories { 22 | google() 23 | jcenter() 24 | } 25 | dependencies { 26 | classpath "com.android.tools.build:gradle:$gradleVersion" 27 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 28 | 29 | // NOTE: Do not place your application dependencies here; they belong 30 | // in the individual module build.gradle files 31 | } 32 | } 33 | 34 | allprojects { 35 | repositories { 36 | google() 37 | jcenter() 38 | } 39 | } 40 | 41 | task clean(type: Delete) { 42 | delete rootProject.buildDir 43 | } 44 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | android.enableJetifier=true 10 | android.useAndroidX=true 11 | org.gradle.jvmargs=-Xmx1536m 12 | # When configured, Gradle will run in incubating parallel mode. 13 | # This option should only be used with decoupled projects. More details, visit 14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 15 | # org.gradle.parallel=true 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitvale/DroidMotion/8bf3d8e269fd97d737932298a1959ff34f42a59b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 12 22:37:27 EET 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------