Repository: serbelga/Material-Motion-Samples Branch: main Commit: 484b191bf6b2 Files: 122 Total size: 382.5 KB Directory structure: gitextract_nt8w2kbz/ ├── .editorconfig ├── .github/ │ └── workflows/ │ └── android.yml ├── .gitignore ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── dev/ │ │ └── sergiobelda/ │ │ └── samples/ │ │ └── materialmotion/ │ │ ├── MainActivity.kt │ │ ├── databinding/ │ │ │ └── BindingAdapters.kt │ │ ├── messages/ │ │ │ ├── ContactsAdapter.kt │ │ │ ├── Messages.kt │ │ │ ├── MessagesActivity.kt │ │ │ └── MessagesAdapter.kt │ │ ├── music/ │ │ │ ├── AlbumFragment.kt │ │ │ ├── AlbumsAdapter.kt │ │ │ ├── AlbumsFragment.kt │ │ │ ├── ArtistsAdapter.kt │ │ │ ├── ArtistsFragment.kt │ │ │ ├── MusicActivity.kt │ │ │ ├── MusicData.kt │ │ │ └── PlaylistsFragment.kt │ │ ├── notes/ │ │ │ ├── AddNoteActivity.kt │ │ │ ├── Note.kt │ │ │ ├── NoteDetailActivity.kt │ │ │ ├── NotesActivity.kt │ │ │ └── NotesAdapter.kt │ │ ├── planets/ │ │ │ ├── PlanetFragment.kt │ │ │ ├── Planets.kt │ │ │ ├── PlanetsActivity.kt │ │ │ └── StepAdapter.kt │ │ ├── sample/ │ │ │ ├── SampleItem.kt │ │ │ └── SamplesAdapter.kt │ │ ├── signin/ │ │ │ ├── SignInActivity.kt │ │ │ ├── SignInFragment.kt │ │ │ └── WelcomeFragment.kt │ │ └── walkthrough/ │ │ ├── WalkthroughActivity.kt │ │ └── WalkthroughFragment.kt │ └── res/ │ ├── color/ │ │ └── step_selector.xml │ ├── drawable/ │ │ ├── circle.xml │ │ ├── ic_baseline_all_inclusive_24.xml │ │ ├── ic_baseline_person_outline_24.xml │ │ ├── ic_baseline_playlist_play_24.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_outline_album_24.xml │ │ ├── ic_outline_edit_24.xml │ │ ├── ic_outline_play_arrow_24.xml │ │ ├── ic_round_account_circle_24.xml │ │ ├── ic_round_add_24.xml │ │ ├── ic_round_clear_24.xml │ │ ├── ic_round_person_24.xml │ │ ├── ic_round_save_24.xml │ │ ├── step_selector.xml │ │ ├── tab_dot.xml │ │ ├── tab_dot_background.xml │ │ ├── undraw_celebration.xml │ │ ├── undraw_drag.xml │ │ ├── undraw_social_sharing.xml │ │ └── undraw_winners.xml │ ├── drawable-v24/ │ │ └── ic_launcher_foreground.xml │ ├── font/ │ │ ├── metropolis.xml │ │ ├── metropolis_medium.otf │ │ ├── metropolis_regular.otf │ │ └── metropolis_semibold.otf │ ├── layout/ │ │ ├── add_note_activity.xml │ │ ├── album_fragment.xml │ │ ├── albums_fragment.xml │ │ ├── artists_fragment.xml │ │ ├── item_album.xml │ │ ├── item_artist.xml │ │ ├── item_contact.xml │ │ ├── item_message.xml │ │ ├── item_note.xml │ │ ├── item_sample.xml │ │ ├── item_step.xml │ │ ├── main_activity.xml │ │ ├── messages_activity.xml │ │ ├── music_activity.xml │ │ ├── note_detail_activity.xml │ │ ├── notes_activity.xml │ │ ├── planet_fragment.xml │ │ ├── planets_activity.xml │ │ ├── playlists_fragment.xml │ │ ├── sign_in_activity.xml │ │ ├── sign_in_fragment.xml │ │ ├── walkthrough_activity.xml │ │ ├── walkthrough_fragment.xml │ │ └── welcome_fragment.xml │ ├── menu/ │ │ └── navigation_menu.xml │ ├── mipmap-anydpi-v26/ │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── navigation/ │ │ └── nav_graph.xml │ └── values/ │ ├── colors.xml │ ├── dimens.xml │ ├── font_certs.xml │ ├── ic_launcher_background.xml │ ├── shape.xml │ ├── strings.xml │ ├── styles.xml │ └── type.xml ├── build-logic/ │ ├── .gitignore │ ├── convention/ │ │ ├── build.gradle.kts │ │ └── src/ │ │ └── main/ │ │ └── kotlin/ │ │ └── SpotlessConventionPlugin.kt │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle.kts ├── build.gradle.kts ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── settings.gradle.kts └── spotless/ ├── copyright.kt ├── copyright.kts └── copyright.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ [*.{kt,kts}] ktlint_standard_backing-property-naming = disabled ================================================ FILE: .github/workflows/android.yml ================================================ name: Android CI on: push: branches: - main - develop pull_request: branches: - main - develop jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: set up JDK 17 uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' cache: gradle - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx /.idea/ /.kotlin/ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2021 Sergio Belda Galbis Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================

Material Motion Samples


A collection of samples using Material Components Motion for Android

Messages

Use Material Fade transition to show the Floating Action Button and Material Container Transform to transform the FAB into a menu

Notes

Use Material Container Transform to transform a FAB into an Activity and a Material Card View into an Activity

Walkthrough

Use Material Shared Axis X transition to introduce the main features of an Application

Solar System

Use Material Shared Axis Y transition in a Material Stepper

Sign In

Use Material Shared Axis Z to represent App's hierarchy. For example, a transition between the application when the user logs in or not

Music

Use Material Fade Through to switch between views that do not have a strong relationship to each other and Material Container Transform with Android Navigation Component

================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle.kts ================================================ @Suppress("DSL_SCOPE_VIOLATION") plugins { alias(libs.plugins.androidApplication) kotlin("android") kotlin("kapt") alias(libs.plugins.ksp) alias(libs.plugins.navigationSafeArgs) id("samples.materialmotion.spotless") } android { compileSdk = 36 defaultConfig { applicationId = "dev.sergiobelda.samples.materialmotion" minSdk = 24 targetSdk = 36 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } buildFeatures { viewBinding = true dataBinding = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlin { jvmToolchain(17) } namespace = "dev.sergiobelda.samples.materialmotion" } dependencies { implementation(libs.androidx.appcompat) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core.coreKtx) implementation(libs.androidx.navigation.navigationFragmentKtx) implementation(libs.androidx.navigation.navigationUiKtx) implementation(libs.androidx.paletteKtx) implementation(libs.google.material) implementation(libs.glide) ksp(libs.glide.compiler) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.test.espresso.core) testImplementation(libs.junit) } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.kts. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/MainActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import dev.sergiobelda.samples.materialmotion.databinding.MainActivityBinding import dev.sergiobelda.samples.materialmotion.sample.SampleItem import dev.sergiobelda.samples.materialmotion.sample.SamplesAdapter import dev.sergiobelda.samples.materialmotion.sample.samples class MainActivity : AppCompatActivity() { private lateinit var binding: MainActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MainActivityBinding.inflate(layoutInflater) setContentView(binding.root) val samplesAdapter = SamplesAdapter(samples).apply { listener = object : SamplesAdapter.OnSampleClickListener { override fun onSampleClick(sampleItem: SampleItem) { startActivity(Intent(this@MainActivity, sampleItem.activity)) } } } binding.recyclerView.apply { layoutManager = LinearLayoutManager(this@MainActivity) adapter = samplesAdapter } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/databinding/BindingAdapters.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.databinding import android.widget.ImageView import androidx.databinding.BindingAdapter import com.bumptech.glide.Glide @BindingAdapter("image_url") fun imageUrl( imageView: ImageView, url: String, ) = Glide .with(imageView) .load(url) .into(imageView) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/messages/ContactsAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.messages import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import dev.sergiobelda.samples.materialmotion.databinding.ItemContactBinding class ContactsAdapter( private val items: List, ) : RecyclerView.Adapter() { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemContactBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) inner class ViewHolder( val binding: ItemContactBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(contact: Contact) { binding.contact = contact } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/messages/Messages.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.messages data class Message( val id: Int, val contact: Contact, val body: String, ) data class Contact( val id: Int, val name: String, val email: String, val image: String, ) val contact1 = Contact( id = 1, name = "Jacob", email = "jacob@mail.com", image = "https://picsum.photos/id/1012/300/300.jpg", ) val contact2 = Contact( id = 2, name = "Sophie", email = "sophie@mail.com", image = "https://picsum.photos/id/237/300/300.jpg", ) val contact3 = Contact( id = 3, name = "Ada", email = "ada@mail.com", image = "https://picsum.photos/id/786/300/300.jpg", ) val contact4 = Contact( id = 4, name = "William", email = "william@mail.com", image = "https://picsum.photos/id/1035/300/300.jpg", ) val message1 = Message( id = 1, contact = contact1, body = "Hello Friend", ) val message2 = Message( id = 2, contact = contact2, body = "Hi!", ) val message3 = Message( id = 3, contact = contact3, body = "...", ) val message4 = Message( id = 4, contact = contact4, body = "Hey!", ) val contacts = listOf(contact1, contact2, contact3, contact4) val favContacts = listOf(contact1, contact2, contact3) val messages = listOf(message1, message2, message3, message4) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/messages/MessagesActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.messages import android.graphics.Color import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionManager import com.google.android.material.color.MaterialColors import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialFade import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.MessagesActivityBinding class MessagesActivity : AppCompatActivity() { private lateinit var binding: MessagesActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MessagesActivityBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(false) initClickListeners() initRecyclerViews() binding.floatingActionButton.post { val transition = MaterialFade().apply { duration = 2000 } TransitionManager.beginDelayedTransition(findViewById(android.R.id.content), transition) binding.floatingActionButton.visibility = View.VISIBLE } } private fun initClickListeners() { binding.floatingActionButton.setOnClickListener { val transition = buildContainerTransformation() transition.startView = binding.floatingActionButton transition.endView = binding.card transition.addTarget(binding.card) TransitionManager.beginDelayedTransition(findViewById(android.R.id.content), transition) binding.card.visibility = View.VISIBLE binding.fabScrim.visibility = View.VISIBLE binding.floatingActionButton.visibility = View.INVISIBLE } binding.fabScrim.setOnClickListener { val transition = buildContainerTransformation() transition.startView = binding.card transition.endView = binding.floatingActionButton transition.addTarget(binding.floatingActionButton) TransitionManager.beginDelayedTransition(binding.coordinator, transition) binding.card.visibility = View.INVISIBLE binding.fabScrim.visibility = View.INVISIBLE binding.floatingActionButton.visibility = View.VISIBLE } } private fun initRecyclerViews() { binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = MessagesAdapter(messages) binding.recyclerView.addOnScrollListener( object : RecyclerView.OnScrollListener() { /* // When scroll down FAB disappears, when scroll up FAB appears override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (dy > 0) { binding.floatingActionButton.hide() } else { binding.floatingActionButton.show() } } */ // When is dragging FAB disappears, when stops FAB appears override fun onScrollStateChanged( recyclerView: RecyclerView, newState: Int, ) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { binding.floatingActionButton.show() } else { binding.floatingActionButton.hide() } } }, ) binding.cardRecyclerView.layoutManager = LinearLayoutManager(this) binding.cardRecyclerView.adapter = ContactsAdapter(favContacts) } private fun buildContainerTransformation() = MaterialContainerTransform().apply { containerColor = MaterialColors.getColor(binding.root, R.attr.colorSecondary) scrimColor = Color.TRANSPARENT duration = 300 interpolator = FastOutSlowInInterpolator() fadeMode = MaterialContainerTransform.FADE_MODE_IN } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/messages/MessagesAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.messages import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import dev.sergiobelda.samples.materialmotion.databinding.ItemMessageBinding class MessagesAdapter( val items: List, ) : RecyclerView.Adapter() { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemMessageBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) inner class ViewHolder( val binding: ItemMessageBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(message: Message) { binding.message = message } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/AlbumFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.navigation.fragment.navArgs import androidx.palette.graphics.Palette import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.google.android.material.color.MaterialColors import com.google.android.material.transition.MaterialContainerTransform import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.AlbumFragmentBinding class AlbumFragment : Fragment() { private val args: AlbumFragmentArgs by navArgs() private lateinit var binding: AlbumFragmentBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { binding = DataBindingUtil.inflate(inflater, R.layout.album_fragment, container, false) binding.lifecycleOwner = viewLifecycleOwner return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) binding.albumFragmentRoot.transitionName = args.albumId.toString() val album = albums.find { it.id == args.albumId } binding.album = album Glide .with(requireContext()) .asBitmap() .load(album?.image) .into( object : CustomTarget() { override fun onResourceReady( resource: Bitmap, transition: Transition?, ) { val palette = Palette.from(resource).generate() palette.darkVibrantSwatch?.let { binding.collapsingToolbar.setBackgroundColor(it.rgb) val color = requireContext().getColor(R.color.colorOnPrimary) binding.collapsingToolbar.setCollapsedTitleTextColor(color) binding.collapsingToolbar.setExpandedTitleColor(color) } ?: palette.lightVibrantSwatch?.let { binding.collapsingToolbar.setBackgroundColor(it.rgb) } } override fun onLoadCleared(placeholder: Drawable?) {} }, ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = buildContainerTransform(true) sharedElementReturnTransition = buildContainerTransform(false) } private fun buildContainerTransform(entering: Boolean) = MaterialContainerTransform(requireContext(), entering).apply { drawingViewId = R.id.nav_host_fragment interpolator = FastOutSlowInInterpolator() containerColor = MaterialColors.getColor( requireActivity().findViewById(android.R.id.content), R.attr.colorSurface, ) fadeMode = MaterialContainerTransform.FADE_MODE_OUT duration = 300 } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/AlbumsAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.ViewGroup import androidx.palette.graphics.Palette import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.google.android.material.card.MaterialCardView import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.ItemAlbumBinding class AlbumsAdapter( private val items: List, private val context: Context, ) : RecyclerView.Adapter() { lateinit var albumClickListener: AlbumClickListener inner class ViewHolder( val binding: ItemAlbumBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(album: Album) { binding.album = album Glide .with(context) .asBitmap() .load(album.image) .into( object : CustomTarget() { override fun onResourceReady( resource: Bitmap, transition: Transition?, ) { val palette = Palette.from(resource).generate() palette.darkVibrantSwatch?.let { binding.albumCard.setCardBackgroundColor(it.rgb) val color = context.getColor(R.color.colorOnPrimary) binding.albumName.setTextColor(color) } ?: palette.lightVibrantSwatch?.let { binding.albumCard.setCardBackgroundColor(it.rgb) } } override fun onLoadCleared(placeholder: Drawable?) {} }, ) binding.albumCard.transitionName = album.id.toString() binding.albumCard.setOnClickListener { albumClickListener.onAlbumClick(album.id, binding.albumCard) } } } override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemAlbumBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) interface AlbumClickListener { fun onAlbumClick( id: Int, cardView: MaterialCardView, ) } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/AlbumsFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.card.MaterialCardView import com.google.android.material.transition.MaterialFadeThrough import dev.sergiobelda.samples.materialmotion.databinding.AlbumsFragmentBinding class AlbumsFragment : Fragment() { private var _binding: AlbumsFragmentBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enterTransition = MaterialFadeThrough() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = AlbumsFragmentBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) postponeEnterTransition() binding.recyclerView.post { startPostponedEnterTransition() } val adapter = AlbumsAdapter(albums, requireContext()) adapter.albumClickListener = object : AlbumsAdapter.AlbumClickListener { override fun onAlbumClick( id: Int, cardView: MaterialCardView, ) { val extras = FragmentNavigatorExtras( cardView to id.toString(), ) val action = AlbumsFragmentDirections.navToAlbumFragment(id) findNavController().navigate(action, extras) } } binding.recyclerView.layoutManager = GridLayoutManager(requireContext(), 2) binding.recyclerView.adapter = adapter } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/ArtistsAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import dev.sergiobelda.samples.materialmotion.databinding.ItemArtistBinding class ArtistsAdapter( var items: List, ) : RecyclerView.Adapter() { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemArtistBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) inner class ViewHolder( var binding: ItemArtistBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(artist: Artist) { binding.artist = artist } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/ArtistsFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.transition.MaterialFadeThrough import dev.sergiobelda.samples.materialmotion.databinding.ArtistsFragmentBinding class ArtistsFragment : Fragment() { private var _binding: ArtistsFragmentBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enterTransition = MaterialFadeThrough() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = ArtistsFragmentBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) postponeEnterTransition() binding.recyclerView.post { startPostponedEnterTransition() } binding.recyclerView.layoutManager = GridLayoutManager(requireContext(), 2) binding.recyclerView.adapter = ArtistsAdapter(artists) } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/MusicActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.navigation.findNavController import androidx.navigation.ui.NavigationUI import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.MusicActivityBinding class MusicActivity : AppCompatActivity() { private lateinit var binding: MusicActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MusicActivityBinding.inflate(layoutInflater) setContentView(binding.root) binding.bottomNavigationView.inflateMenu(R.menu.navigation_menu) val navController = findNavController(R.id.nav_host_fragment) NavigationUI.setupWithNavController( binding.bottomNavigationView, navController, ) navController.addOnDestinationChangedListener { _, destination, _ -> when (destination.id) { R.id.albumFragment -> { binding.appbarLayout.setExpanded(false, true) binding.bottomNavigationView.visibility = View.GONE } else -> { binding.appbarLayout.setExpanded(true, true) binding.bottomNavigationView.visibility = View.VISIBLE } } } } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/MusicData.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("ktlint:standard:max-line-length") package dev.sergiobelda.samples.materialmotion.music data class Artist( val id: Int, val name: String, val image: String, ) data class Album( val id: Int, val name: String, val image: String, ) val shiroi = Album( id = 1, name = "Shiroi", image = "https://img.discogs.com/EI7SfveNHws0ONsXJO9FLEpZFYc=/fit-in/600x536/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/R-12841514-1544304275-9214.jpeg.jpg", ) val wywh = Album( id = 2, name = "Wish You Were Here", image = "https://img.discogs.com/tQj6wwY11KUz3TUwq8DHOnZdWl0=/fit-in/600x600/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/R-3354750-1477237778-6311.jpeg.jpg", ) val jazzmaica = Album( id = 3, name = "Jazzmaica", image = "https://img.discogs.com/JQny678Vd6janet-PeNjrlK66z8=/fit-in/600x599/filters:strip_icc():format(jpeg):mode_rgb():quality(90)/discogs-images/R-8489874-1510971569-9261.jpeg.jpg", ) val albums = listOf(shiroi, wywh, jazzmaica) val snarky = Artist( id = 1, name = "Snarky Puppy", image = "https://is2-ssl.mzstatic.com/image/thumb/Features20/v4/bf/82/f8/bf82f863-1edd-626a-2d9b-8c76cbabdd83/mzl.pouxzkfo.jpg/1000x1000cc.jpg", ) val yussef = Artist( id = 2, name = "Yussef Dayes", image = "https://is3-ssl.mzstatic.com/image/thumb/Music123/v4/ca/c7/96/cac7966f-dd10-d016-b146-6d8e8cf8fb70/pr_source.png/1000x1000cc.jpg", ) val dubinc = Artist( id = 3, name = "Dub Inc", image = "https://is3-ssl.mzstatic.com/image/thumb/Music123/v4/34/75/9f/34759f94-c43b-1861-8fc3-12b2e40198b1/pr_source.png/1000x1000cc.jpg", ) val cory = Artist( id = 4, name = "Cory Henry", image = "https://is1-ssl.mzstatic.com/image/thumb/Music123/v4/70/5e/fe/705efec8-c546-0534-64fa-77ee0c8fd99d/pr_source.png/1000x1000cc.jpg", ) val artists = listOf(cory, dubinc, snarky, yussef) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/music/PlaylistsFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.music import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import com.google.android.material.transition.MaterialFadeThrough import dev.sergiobelda.samples.materialmotion.R class PlaylistsFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enterTransition = MaterialFadeThrough() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View = inflater.inflate(R.layout.playlists_fragment, container, false) } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/notes/AddNoteActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.notes import android.os.Bundle import android.view.MenuItem import android.view.Window import androidx.appcompat.app.AppCompatActivity import androidx.interpolator.view.animation.FastOutSlowInInterpolator import com.google.android.material.color.MaterialColors import com.google.android.material.transition.platform.MaterialArcMotion import com.google.android.material.transition.platform.MaterialContainerTransform import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.AddNoteActivityBinding class AddNoteActivity : AppCompatActivity() { private lateinit var binding: AddNoteActivityBinding override fun onCreate(savedInstanceState: Bundle?) { window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) binding = AddNoteActivityBinding.inflate(layoutInflater) binding.coordinator.transitionName = "shared_element_end_root" setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) window.sharedElementEnterTransition = buildContainerTransform() window.sharedElementReturnTransition = buildContainerTransform() setContentView(binding.coordinator) super.onCreate(savedInstanceState) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_round_clear_24) supportActionBar?.setDisplayShowTitleEnabled(false) } private fun buildContainerTransform() = MaterialContainerTransform().apply { addTarget(binding.coordinator) setAllContainerColors(MaterialColors.getColor(binding.root, R.attr.colorSurface)) pathMotion = MaterialArcMotion() duration = 500 interpolator = FastOutSlowInInterpolator() fadeMode = MaterialContainerTransform.FADE_MODE_IN } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/notes/Note.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("ktlint:standard:max-line-length") package dev.sergiobelda.samples.materialmotion.notes import androidx.annotation.ColorRes import dev.sergiobelda.samples.materialmotion.R data class Note( val id: Int, val title: String, val body: String, @ColorRes val colorRes: Int, ) val note1 = Note( id = 1, title = "ToDo 1", body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus scelerisque placerat nisl, nec semper felis ullamcorper vel. Nullam egestas ante nec tortor egestas mattis. Duis ut diam nec nibh sodales commodo at at diam. Nunc tempor eu lectus ut feugiat. Etiam eget ullamcorper est, at scelerisque lectus. Aliquam erat volutpat. Maecenas est urna, vestibulum non eros non, dignissim feugiat mi. Cras sit amet ex hendrerit, accumsan dolor in, bibendum erat. Maecenas ullamcorper ut risus eget congue. Vestibulum aliquam ipsum ut turpis efficitur, vel malesuada neque aliquam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean egestas justo id rutrum blandit. Vestibulum id orci libero. Aliquam pharetra sed mauris ac vehicula.", colorRes = R.color.blue300, ) val note2 = Note( id = 2, title = "ToDo 2", body = "Fusce hendrerit enim in eros congue, sed pharetra libero tempus. Integer accumsan euismod nibh non vestibulum. Curabitur finibus imperdiet nunc vel ornare. Ut maximus fringilla sapien in viverra. Aenean a nulla feugiat, hendrerit risus et, congue erat. Ut venenatis lorem sit amet volutpat sollicitudin. Donec ac lorem auctor sem mattis faucibus non ac ante. Phasellus id sem non ante bibendum porta non in tellus. Etiam pellentesque porta luctus.", colorRes = R.color.amber300, ) val note3 = Note( id = 3, title = "ToDo 3", body = "Praesent interdum dictum magna quis pretium. Suspendisse at cursus ante, id rutrum nunc. Nullam at lacinia nibh, nec gravida lectus. Quisque maximus vulputate leo, et sollicitudin turpis luctus pellentesque. Nullam vehicula sagittis magna, consectetur congue neque ullamcorper vel. Praesent sed vulputate nunc. Ut ligula lorem, lobortis tristique interdum at, mollis a dolor. Ut sed fringilla urna, id suscipit justo. Praesent ut tortor pharetra, laoreet metus non, iaculis mi. Duis vel lacus fermentum, porta neque id, ultricies arcu. Nunc interdum, est ac sodales tristique, elit urna feugiat dui, quis venenatis ante lorem ullamcorper mi. Nulla id condimentum lorem. Nunc ac scelerisque felis. Nam semper, mi et ultrices rutrum, neque odio molestie tortor, ac iaculis ante arcu eu elit. Phasellus imperdiet tortor quis aliquam ornare.", colorRes = R.color.green300, ) val notes = listOf(note1, note2, note3) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/notes/NoteDetailActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.notes import android.os.Bundle import android.view.MenuItem import android.view.Window import androidx.appcompat.app.AppCompatActivity import androidx.interpolator.view.animation.FastOutSlowInInterpolator import com.google.android.material.transition.platform.MaterialContainerTransform import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import dev.sergiobelda.samples.materialmotion.databinding.NoteDetailActivityBinding class NoteDetailActivity : AppCompatActivity() { private lateinit var binding: NoteDetailActivityBinding override fun onCreate(savedInstanceState: Bundle?) { window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) binding = NoteDetailActivityBinding.inflate(layoutInflater) val noteId = intent.getIntExtra("noteId", 0) binding.coordinator.transitionName = noteId.toString() setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback()) window.sharedElementEnterTransition = buildContainerTransform() window.sharedElementReturnTransition = buildContainerTransform() setContentView(binding.root) super.onCreate(savedInstanceState) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(false) val note = notes.find { it.id == noteId } binding.note = note } private fun buildContainerTransform() = MaterialContainerTransform().apply { addTarget(binding.coordinator) duration = 300 interpolator = FastOutSlowInInterpolator() fadeMode = MaterialContainerTransform.FADE_MODE_IN } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/notes/NotesActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.notes import android.content.Intent import android.os.Bundle import android.view.MenuItem import android.view.Window import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityOptionsCompat import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.card.MaterialCardView import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback import dev.sergiobelda.samples.materialmotion.databinding.NotesActivityBinding class NotesActivity : AppCompatActivity() { private lateinit var binding: NotesActivityBinding override fun onCreate(savedInstanceState: Bundle?) { window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) binding = NotesActivityBinding.inflate(layoutInflater) setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback()) window.sharedElementsUseOverlay = false setContentView(binding.root) super.onCreate(savedInstanceState) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(false) setClickListeners() setRecyclerView() } private fun setRecyclerView() { val adapter = NotesAdapter(notes) adapter.noteClickListener = object : NotesAdapter.NoteClickListener { override fun onNoteClick( id: Int, noteCard: MaterialCardView, ) { val intent = Intent(this@NotesActivity, NoteDetailActivity::class.java) val options = ActivityOptionsCompat.makeSceneTransitionAnimation( this@NotesActivity, noteCard, id.toString(), ) intent.putExtra("noteId", id) startActivity(intent, options.toBundle()) } } binding.recyclerView.layoutManager = GridLayoutManager(this, 2) binding.recyclerView.adapter = adapter } private fun setClickListeners() { binding.addNoteFab.setOnClickListener { val intent = Intent(this, AddNoteActivity::class.java) val options = ActivityOptionsCompat.makeSceneTransitionAnimation( this, binding.addNoteFab, "shared_element_end_root", ) startActivity(intent, options.toBundle()) } } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/notes/NotesAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.notes import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.google.android.material.card.MaterialCardView import dev.sergiobelda.samples.materialmotion.databinding.ItemNoteBinding class NotesAdapter( private val items: List, ) : RecyclerView.Adapter() { lateinit var noteClickListener: NoteClickListener override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemNoteBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) inner class ViewHolder( val binding: ItemNoteBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(note: Note) { binding.note = note binding.noteCard.transitionName = note.id.toString() binding.noteCard.setOnClickListener { noteClickListener.onNoteClick(note.id, binding.noteCard) } } } interface NoteClickListener { fun onNoteClick( id: Int, noteCard: MaterialCardView, ) } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/planets/PlanetFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.planets import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.PlanetFragmentBinding private const val ARG_PLANET_ID = "planet_id" /** * A simple [Fragment] subclass. * Use the [PlanetFragment.newInstance] factory method to * create an instance of this fragment. */ class PlanetFragment : Fragment() { private lateinit var binding: PlanetFragmentBinding private var planetId: Int? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { planetId = it.getInt(ARG_PLANET_ID) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { binding = DataBindingUtil.inflate(inflater, R.layout.planet_fragment, container, false) binding.lifecycleOwner = viewLifecycleOwner return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) val planet = planets.singleOrNull { it.id == planetId } planet?.let { binding.planet = it } } companion object { /** * @param planetId * @return A new instance of fragment StepFragment. */ @JvmStatic fun newInstance(planetId: Int) = PlanetFragment().apply { arguments = Bundle().apply { putInt(ARG_PLANET_ID, planetId) } } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/planets/Planets.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("ktlint:standard:max-line-length") package dev.sergiobelda.samples.materialmotion.planets data class Planet( val id: Int, val name: String, val mainImage: String, val description: String, ) val mercury = Planet( id = 1, name = "Mercury", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/771_PIA16853.jpg", description = "The smallest planet in our solar system and nearest to the Sun, Mercury is only slightly larger than Earth's Moon." + "\n" + "From the surface of Mercury, the Sun would appear more than three times as large as it does when viewed from Earth, and the sunlight would be as much as seven times brighter. Despite its proximity to the Sun, Mercury is not the hottest planet in our solar system – that title belongs to nearby Venus, thanks to its dense atmosphere.", ) val venus = Planet( id = 2, name = "Venus", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/775_PIA00271_detail.jpg", description = "Second planet from the Sun and our closest planetary neighbor, Venus is similar in structure and size to Earth, but it is now a very different world. Venus spins slowly in the opposite direction most planets do. Its thick atmosphere traps heat in a runaway greenhouse effect, making it the hottest planet in our solar system—with surface temperatures hot enough to melt lead. Glimpses below the clouds reveal volcanoes and deformed mountains.", ) val earth = Planet( id = 3, name = "Earth", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/2292_as08-14-2383.jpg", description = "Our home planet is the third planet from the Sun, and the only place we know of so far that’s inhabited by living things." + "\n" + "While Earth is only the fifth largest planet in the solar system, it is the only world in our solar system with liquid water on the surface. Just slightly larger than nearby Venus, Earth is the biggest of the four planets closest to the Sun, all of which are made of rock and metal.\n" + "\n" + "The name Earth is at least 1,000 years old. All of the planets, except for Earth, were named after Greek and Roman gods and goddesses. However, the name Earth is a Germanic word, which simply means “the ground.”", ) val mars = Planet( id = 4, name = "Mars", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/948_mars_july18.jpg", description = "The fourth planet from the Sun, Mars is a dusty, cold, desert world with a very thin atmosphere." + "\n" + "This dynamic planet has seasons, polar ice caps and weather and canyons and extinct volcanoes, evidence of an even more active past.\n" + "\n" + "Mars is one of the most explored bodies in our solar system, and it's the only planet where we've sent rovers to roam the alien landscape. NASA currently has three spacecraft in orbit, one rover and one lander on the surface and another rover under construction here on Earth. India and ESA also have spacecraft in orbit above Mars.\n" + "\n" + "These robotic explorers have found lots of evidence that Mars was much wetter and warmer, with a thicker atmosphere, billions of years ago.", ) val jupiter = Planet( id = 5, name = "Jupiter", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/2486_stsci-h-p1936a_1800.jpg", description = "Jupiter has a long history surprising scientists—all the way back to 1610 when Galileo Galilei found the first moons beyond Earth. That discovery changed the way we see the universe." + "Fifth in line from the Sun, Jupiter is, by far, the largest planet in the solar system – more than twice as massive as all the other planets combined.\n" + "\n" + "Jupiter's familiar stripes and swirls are actually cold, windy clouds of ammonia and water, floating in an atmosphere of hydrogen and helium. Jupiter’s iconic Great Red Spot is a giant storm bigger than Earth that has raged for hundreds of years.\n" + "\n" + "One spacecraft — NASA's Juno orbiter — is currently exploring this giant world.", ) val saturn = Planet( id = 6, name = "Saturn", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/2490_stsci-h-p1943a-f_1200.jpg", description = "Saturn is the sixth planet from the Sun and the second largest planet in our solar system." + "\n" + "Adorned with thousands of beautiful ringlets, Saturn is unique among the planets. It is not the only planet to have rings—made of chunks of ice and rock—but none are as spectacular or as complicated as Saturn's.\n" + "\n" + "Like fellow gas giant Jupiter, Saturn is a massive ball made mostly of hydrogen and helium.", ) val uranus = Planet( id = 7, name = "Uranus", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/599_PIA18182.jpg", description = "The first planet found with the aid of a telescope, Uranus was discovered in 1781 by astronomer William Herschel, although he originally thought it was either a comet or a star." + "\n" + "It was two years later that the object was universally accepted as a new planet, in part because of observations by astronomer Johann Elert Bode. Herschel tried unsuccessfully to name his discovery Georgium Sidus after King George III. Instead the scientific community accepted Bode's suggestion to name it Uranus, the Greek god of the sky, as suggested by Bode.", ) val neptune = Planet( id = 8, name = "Neptune", mainImage = "https://solarsystem.nasa.gov/system/resources/detail_files/611_PIA01492.jpg", description = "Dark, cold and whipped by supersonic winds, ice giant Neptune is the eighth and most distant planet in our solar system." + "\n" + "More than 30 times as far from the Sun as Earth, Neptune is the only planet in our solar system not visible to the naked eye and the first predicted by mathematics before its discovery. In 2011 Neptune completed its first 165-year orbit since its discovery in 1846.\n" + "\n" + "NASA's Voyager 2 is the only spacecraft to have visited Neptune up close. It flew past in 1989 on its way out of the solar system.", ) val planets = arrayListOf(mercury, venus, earth, mars, jupiter, saturn, uranus, neptune) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/planets/PlanetsActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.planets import android.os.Bundle import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.commit import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.transition.MaterialSharedAxis import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.PlanetsActivityBinding class PlanetsActivity : AppCompatActivity() { private lateinit var binding: PlanetsActivityBinding private var selected = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = PlanetsActivityBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowTitleEnabled(false) val fragment = PlanetFragment.newInstance(planets[selected].id) supportFragmentManager.commit { add(R.id.fragment_container, fragment, FRAGMENT_TAG) } setRecyclerView() } private fun setRecyclerView() { val adapter = StepAdapter(planets) adapter.stepClickListener = object : StepAdapter.StepClickListener { override fun onStepClick(position: Int) { val forward = position >= selected selected = position val previousFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) previousFragment?.exitTransition = buildTransition(forward) val fragment = PlanetFragment.newInstance(planets[selected].id) fragment.enterTransition = buildTransition(forward) supportFragmentManager.commit { replace(R.id.fragment_container, fragment, FRAGMENT_TAG) } } } binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { onBackPressedDispatcher.onBackPressed() true } else -> { true } } private fun buildTransition(forward: Boolean) = MaterialSharedAxis(MaterialSharedAxis.Y, forward).apply { duration = 500 } companion object { private const val FRAGMENT_TAG = "PLANET_FRAGMENT" } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/planets/StepAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.planets import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import dev.sergiobelda.samples.materialmotion.databinding.ItemStepBinding class StepAdapter( val items: List, ) : RecyclerView.Adapter() { lateinit var stepClickListener: StepClickListener private var selected = 0 override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemStepBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(position) inner class ViewHolder( val binding: ItemStepBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(position: Int) { if (position == items.size - 1) { binding.separator.visibility = View.GONE } else { binding.separator.visibility = View.VISIBLE } binding.stepName.isSelected = position == selected binding.stepButton.isSelected = position == selected binding.stepName.text = position.toString() binding.stepButton.setOnClickListener { val aux = selected selected = position stepClickListener.onStepClick(position) notifyItemChanged(aux) notifyItemChanged(selected) } } } interface StepClickListener { fun onStepClick(position: Int) } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/sample/SampleItem.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.sample import androidx.annotation.StringRes import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.messages.MessagesActivity import dev.sergiobelda.samples.materialmotion.music.MusicActivity import dev.sergiobelda.samples.materialmotion.notes.NotesActivity import dev.sergiobelda.samples.materialmotion.planets.PlanetsActivity import dev.sergiobelda.samples.materialmotion.signin.SignInActivity import dev.sergiobelda.samples.materialmotion.walkthrough.WalkthroughActivity data class SampleItem( @StringRes val nameRes: Int, @StringRes val descriptionRes: Int, val activity: Class<*>, ) val messagesSample = SampleItem( nameRes = R.string.messages, descriptionRes = R.string.messages_card_body, activity = MessagesActivity::class.java, ) val notesSample = SampleItem( nameRes = R.string.notes, descriptionRes = R.string.notes_card_body, activity = NotesActivity::class.java, ) val walkthroughSample = SampleItem( nameRes = R.string.walkthrough, descriptionRes = R.string.walkthrough_card_body, activity = WalkthroughActivity::class.java, ) val solarSystemSample = SampleItem( nameRes = R.string.solar_system, descriptionRes = R.string.solar_system_card_body, activity = PlanetsActivity::class.java, ) val signInSample = SampleItem( nameRes = R.string.sign_in, descriptionRes = R.string.sign_in_card_body, activity = SignInActivity::class.java, ) val musicSample = SampleItem( nameRes = R.string.music, descriptionRes = R.string.music_card_body, activity = MusicActivity::class.java, ) val samples = arrayListOf( messagesSample, notesSample, walkthroughSample, solarSystemSample, signInSample, musicSample, ) ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/sample/SamplesAdapter.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.sample import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import dev.sergiobelda.samples.materialmotion.databinding.ItemSampleBinding class SamplesAdapter( private val items: List, ) : RecyclerView.Adapter() { var listener: OnSampleClickListener? = null override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, ) = ViewHolder( ItemSampleBinding.inflate( LayoutInflater.from(parent.context), parent, false, ), ) override fun getItemCount() = items.size override fun onBindViewHolder( holder: ViewHolder, position: Int, ) = holder.bind(items[position]) inner class ViewHolder( val binding: ItemSampleBinding, ) : RecyclerView.ViewHolder(binding.root) { fun bind(sampleItem: SampleItem) { binding.sample = sampleItem binding.button.setOnClickListener { listener?.onSampleClick(sampleItem) } } } interface OnSampleClickListener { fun onSampleClick(sampleItem: SampleItem) } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/signin/SignInActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.signin import android.os.Bundle import android.view.Window import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.commit import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.SignInActivityBinding class SignInActivity : AppCompatActivity() { private lateinit var binding: SignInActivityBinding override fun onCreate(savedInstanceState: Bundle?) { window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) super.onCreate(savedInstanceState) binding = SignInActivityBinding.inflate(layoutInflater) setContentView(binding.root) val signInFragment = SignInFragment() supportFragmentManager.commit { add(R.id.fragment_container, signInFragment) } } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/signin/SignInFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.signin import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.commit import com.google.android.material.transition.MaterialSharedAxis import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.SignInFragmentBinding class SignInFragment : Fragment() { private var _binding: SignInFragmentBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) exitTransition = buildTransition() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = SignInFragmentBinding.inflate(layoutInflater, container, false) return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) binding.signIn.setOnClickListener { val welcomeFragment = WelcomeFragment() welcomeFragment.enterTransition = buildTransition() parentFragmentManager.commit { replace(R.id.fragment_container, welcomeFragment) } } } private fun buildTransition() = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply { duration = 500 } override fun onDestroyView() { super.onDestroyView() _binding = null } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/signin/WelcomeFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.signin import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.commit import com.google.android.material.transition.MaterialSharedAxis import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.WelcomeFragmentBinding class WelcomeFragment : Fragment() { private var _binding: WelcomeFragmentBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) exitTransition = buildTransition() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = WelcomeFragmentBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) binding.returnButton.setOnClickListener { val signInFragment = SignInFragment() signInFragment.enterTransition = buildTransition() parentFragmentManager.commit { replace(R.id.fragment_container, signInFragment) } } } private fun buildTransition() = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply { duration = 500 } override fun onDestroyView() { super.onDestroyView() _binding = null } companion object { const val TAG = "WelcomeFragment" } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/walkthrough/WalkthroughActivity.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.walkthrough import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.commit import androidx.lifecycle.MutableLiveData import com.google.android.material.transition.MaterialSharedAxis import dev.sergiobelda.samples.materialmotion.R import dev.sergiobelda.samples.materialmotion.databinding.WalkthroughActivityBinding private const val COUNT = 3 class WalkthroughActivity : AppCompatActivity() { private lateinit var binding: WalkthroughActivityBinding private var selected = MutableLiveData().apply { value = 0 } private val titles by lazy { arrayListOf( getString(R.string.upload_your_photos), getString(R.string.share), getString(R.string.invite_friends), ) } private val bodies by lazy { arrayListOf( getString(R.string.upload_artistic_photos), getString(R.string.share_work_social_networks), getString(R.string.enjoy_rewards), ) } private val images = arrayListOf( R.drawable.undraw_drag, R.drawable.undraw_social_sharing, R.drawable.undraw_winners, ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = WalkthroughActivityBinding.inflate(layoutInflater) setContentView(binding.root) selected.value?.let { val fragment = WalkthroughFragment.newInstance( titles[it], bodies[it], images[it], ) supportFragmentManager.commit { add(R.id.fragment_container, fragment, FRAGMENT_TAG) } } setDotsTabLayout() setClickListeners() setSelectedObserver() } private fun setDotsTabLayout() { repeat(COUNT) { binding.tabLayout.addTab(binding.tabLayout.newTab()) } binding.tabLayout.touchables.forEach { it.isEnabled = false } } private fun setClickListeners() { binding.backButton.setOnClickListener { selected.value?.let { selected.value = it - 1 } selectFragment(forward = false) } binding.nextButton.setOnClickListener { selected.value?.let { selected.value = it + 1 } selectFragment(forward = true) } } private fun setSelectedObserver() { selected.observe( this, { binding.nextButton.isEnabled = it < COUNT - 1 binding.backButton.isEnabled = it > 0 binding.tabLayout.apply { selectTab(getTabAt(it)) } }, ) } private fun selectFragment(forward: Boolean) { selected.value?.let { val previousFragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) previousFragment?.exitTransition = buildTransition(forward) val fragment = WalkthroughFragment.newInstance( titles[it], bodies[it], images[it], ) fragment.enterTransition = buildTransition(forward) supportFragmentManager.commit { replace(R.id.fragment_container, fragment, FRAGMENT_TAG) } } } private fun buildTransition(forward: Boolean) = MaterialSharedAxis(MaterialSharedAxis.X, forward).apply { duration = 500 } companion object { private const val FRAGMENT_TAG = "WALKTHROUGH_FRAGMENT" } } ================================================ FILE: app/src/main/java/dev/sergiobelda/samples/materialmotion/walkthrough/WalkthroughFragment.kt ================================================ /* * Copyright 2021 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dev.sergiobelda.samples.materialmotion.walkthrough import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.fragment.app.Fragment import dev.sergiobelda.samples.materialmotion.databinding.WalkthroughFragmentBinding private const val ARG_TITLE = "title" private const val ARG_BODY = "body" private const val ARG_DRAWABLE_RES = "drawableRes" /** * A simple [Fragment] subclass. * Use the [WalkthroughFragment.newInstance] factory method to * create an instance of this fragment. */ class WalkthroughFragment : Fragment() { private var title: String? = null private var body: String? = null private var drawableRes: Int? = null private var _binding: WalkthroughFragmentBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { title = it.getString(ARG_TITLE) body = it.getString(ARG_BODY) drawableRes = it.getInt(ARG_DRAWABLE_RES) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { _binding = WalkthroughFragmentBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated( view: View, savedInstanceState: Bundle?, ) { super.onViewCreated(view, savedInstanceState) binding.titleTextView.text = title binding.bodyTextView.text = body drawableRes?.let { binding.imageView.setImageResource(it) } } override fun onDestroyView() { super.onDestroyView() _binding = null } companion object { /** * Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param body Parameter 1. * @param imageRes Parameter 2. * @return A new instance of fragment WalkthroughFragment. */ @JvmStatic fun newInstance( title: String, body: String, @DrawableRes imageRes: Int, ) = WalkthroughFragment().apply { arguments = Bundle().apply { putString(ARG_TITLE, title) putString(ARG_BODY, body) putInt(ARG_DRAWABLE_RES, imageRes) } } } } ================================================ FILE: app/src/main/res/color/step_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/circle.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_baseline_all_inclusive_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_baseline_person_outline_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_baseline_playlist_play_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_outline_album_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_outline_edit_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_outline_play_arrow_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_round_account_circle_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_round_add_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_round_clear_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_round_person_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_round_save_24.xml ================================================ ================================================ FILE: app/src/main/res/drawable/step_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_dot.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_dot_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/undraw_celebration.xml ================================================ ================================================ FILE: app/src/main/res/drawable/undraw_drag.xml ================================================ ================================================ FILE: app/src/main/res/drawable/undraw_social_sharing.xml ================================================ ================================================ FILE: app/src/main/res/drawable/undraw_winners.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/font/metropolis.xml ================================================ ================================================ FILE: app/src/main/res/layout/add_note_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/album_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/albums_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/artists_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_album.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_artist.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_contact.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_message.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_note.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_sample.xml ================================================ ================================================ FILE: app/src/main/res/layout/item_step.xml ================================================ ================================================ FILE: app/src/main/res/layout/main_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/messages_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/music_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/note_detail_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/notes_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/planet_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/planets_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/playlists_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/sign_in_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/sign_in_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/walkthrough_activity.xml ================================================ ================================================ FILE: app/src/main/res/layout/walkthrough_fragment.xml ================================================ ================================================ FILE: app/src/main/res/layout/welcome_fragment.xml ================================================ ================================================ FILE: app/src/main/res/menu/navigation_menu.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: app/src/main/res/navigation/nav_graph.xml ================================================ ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #2196F3 #0277BD #283593 #66434343 #FFFFFF #FFFFFF #FFFFFF #E7E7E7 #BDBDBD #FAFAFA #202020 #80FFFFFF #4FC3F7 #81C784 #FFB74D ================================================ FILE: app/src/main/res/values/dimens.xml ================================================ 8dp 16dp 8dp ================================================ FILE: app/src/main/res/values/font_certs.xml ================================================ @array/com_google_android_gms_fonts_certs_dev @array/com_google_android_gms_fonts_certs_prod MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK ================================================ FILE: app/src/main/res/values/ic_launcher_background.xml ================================================ #212121 ================================================ FILE: app/src/main/res/values/shape.xml ================================================ ================================================ FILE: app/src/main/res/values/strings.xml ================================================ Material Motion Samples Artists Albums Playlists Messages Solar System Add note Save note Body Title My notes Notes Walkthrough Back Next Upload your photos Share Invite Friends Upload your artistic photos to the app Share your work on social networks You and your friends will enjoy fantastic rewards! Sign In User Password My Library Return Music Use Material Fade transition to show the Floating Action Button and Material Container Transform to transform the FAB into a menu Use Material Container Transform to transform a FAB into an Activity and a Material Card View into an Activity Use Material Shared Axis X transition to introduce the main features of an Application Use Material Shared Axis Y transition in a Material Stepper Use Material Fade Through to switch between views that do not have a strong relationship to each other and Material Container Transform with Android Navigation Component Use Material Shared Axis Z to represent App\'s hierarchy. For example, a transition between the application when the user logs in or not Hi there! Compose New ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/main/res/values/type.xml ================================================ ================================================ FILE: build-logic/.gitignore ================================================ *.iml /.idea/ /build/ /convention/build/ /local.properties .gradle ================================================ FILE: build-logic/convention/build.gradle.kts ================================================ plugins { `kotlin-dsl` } group = "dev.sergiobelda.samples.materialmotion.buildlogic" java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } dependencies { implementation(libs.kotlin.gradlePlugin) implementation(libs.spotless.gradlePlugin) } gradlePlugin { plugins { register("spotless") { id = "samples.materialmotion.spotless" implementationClass = "SpotlessConventionPlugin" } } } ================================================ FILE: build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt ================================================ /* * Copyright 2022 Sergio Belda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.diffplug.gradle.spotless.SpotlessExtension import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.getByType class SpotlessConventionPlugin : Plugin { override fun apply(target: Project) { with(target) { val libs = extensions.getByType().named("libs") val ktlintVersion = libs.findVersion("ktlint").get().toString() pluginManager.apply("com.diffplug.spotless") extensions.configure { kotlin { target("**/*.kt") targetExclude("**/build/**/*.kt") licenseHeaderFile(rootProject.file("spotless/copyright.kt")) // ktlint ktlint(ktlintVersion) } format("kts") { target("**/*.kts") targetExclude("**/build/**/*.kts") // Look for the first line that doesn't have a block comment (assumed to be the license) licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") } format("xml") { target("**/*.xml") targetExclude("**/build/**/*.xml") // Look for the first XML tag that isn't a comment (