├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── layout
│ │ │ │ ├── activity_step4.xml
│ │ │ │ ├── activity_step5.xml
│ │ │ │ ├── activity_step6.xml
│ │ │ │ ├── activity_step7.xml
│ │ │ │ ├── activity_step8.xml
│ │ │ │ ├── activity_step9.xml
│ │ │ │ ├── step6_second_fragment.xml
│ │ │ │ ├── step6_first_fragment.xml
│ │ │ │ ├── step7_profile_fragment.xml
│ │ │ │ ├── step9_profile_fragment.xml
│ │ │ │ ├── activity_step1.xml
│ │ │ │ ├── activity_step2.xml
│ │ │ │ ├── step8_main_fragment.xml
│ │ │ │ ├── step5_first_fragment.xml
│ │ │ │ ├── step4_first_view.xml
│ │ │ │ ├── step8_form_fragment.xml
│ │ │ │ ├── step5_second_fragment.xml
│ │ │ │ ├── step7_enter_profile_data_fragment.xml
│ │ │ │ ├── step9_enter_profile_data_fragment.xml
│ │ │ │ ├── step4_second_view.xml
│ │ │ │ ├── step7_create_login_credentials_fragment.xml
│ │ │ │ ├── step9_create_login_credentials_fragment.xml
│ │ │ │ ├── step7_login_fragment.xml
│ │ │ │ ├── step9_login_fragment.xml
│ │ │ │ ├── activity_step3.xml
│ │ │ │ └── activity_main.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── anim
│ │ │ │ ├── slide_out_to_left.xml
│ │ │ │ ├── slide_out_to_right.xml
│ │ │ │ ├── slide_in_from_left.xml
│ │ │ │ └── slide_in_from_right.xml
│ │ │ ├── drawable
│ │ │ │ ├── ic_arrow_back_black_24dp.xml
│ │ │ │ ├── ic_tag_faces_black_24dp.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── zhuinden
│ │ │ │ └── simplestacktutorials
│ │ │ │ ├── steps
│ │ │ │ ├── step_3
│ │ │ │ │ ├── Step3ButtonConfiguration.kt
│ │ │ │ │ ├── Step3Screen.kt
│ │ │ │ │ ├── Step3SecondScreen.kt
│ │ │ │ │ ├── Step3FirstScreen.kt
│ │ │ │ │ └── Step3Activity.kt
│ │ │ │ ├── step_5
│ │ │ │ │ ├── Step5BaseFragment.kt
│ │ │ │ │ ├── Step5Utils.kt
│ │ │ │ │ ├── Step5FirstScreen.kt
│ │ │ │ │ ├── Step5SecondScreen.kt
│ │ │ │ │ ├── Step5Screen.kt
│ │ │ │ │ ├── Step5FirstFragment.kt
│ │ │ │ │ ├── Step5SecondFragment.kt
│ │ │ │ │ ├── Step5Activity.kt
│ │ │ │ │ └── FragmentStateChanger.java
│ │ │ │ ├── step_6
│ │ │ │ │ ├── Step6BaseFragment.kt
│ │ │ │ │ ├── Step6Utils.kt
│ │ │ │ │ ├── Step6FirstScreen.kt
│ │ │ │ │ ├── Step6SecondScreen.kt
│ │ │ │ │ ├── Step6Screen.kt
│ │ │ │ │ ├── Step6SecondFragment.kt
│ │ │ │ │ ├── Step6FirstFragment.kt
│ │ │ │ │ ├── Step6Activity.kt
│ │ │ │ │ └── FragmentStateChanger.java
│ │ │ │ ├── step_4
│ │ │ │ │ ├── Step4Utils.kt
│ │ │ │ │ ├── Step4FirstScreen.kt
│ │ │ │ │ ├── Step4SecondScreen.kt
│ │ │ │ │ ├── Step4Screen.kt
│ │ │ │ │ ├── Step4FirstView.kt
│ │ │ │ │ ├── Step4Activity.kt
│ │ │ │ │ └── Step4SecondView.kt
│ │ │ │ ├── step_7
│ │ │ │ │ ├── core
│ │ │ │ │ │ ├── navigation
│ │ │ │ │ │ │ ├── BaseFragment.kt
│ │ │ │ │ │ │ ├── NavUtils.kt
│ │ │ │ │ │ │ ├── FragmentKey.kt
│ │ │ │ │ │ │ └── FragmentStateChanger.java
│ │ │ │ │ │ └── viewmodels
│ │ │ │ │ │ │ ├── HasServices.kt
│ │ │ │ │ │ │ └── ViewModelUtils.kt
│ │ │ │ │ ├── features
│ │ │ │ │ │ ├── registration
│ │ │ │ │ │ │ ├── EnterProfileDataKey.kt
│ │ │ │ │ │ │ ├── CreateLoginCredentialsKey.kt
│ │ │ │ │ │ │ ├── EnterProfileDataFragment.kt
│ │ │ │ │ │ │ ├── CreateLoginCredentialsFragment.kt
│ │ │ │ │ │ │ └── RegistrationViewModel.kt
│ │ │ │ │ │ ├── login
│ │ │ │ │ │ │ ├── LoginKey.kt
│ │ │ │ │ │ │ ├── LoginViewModel.kt
│ │ │ │ │ │ │ └── LoginFragment.kt
│ │ │ │ │ │ └── profile
│ │ │ │ │ │ │ ├── ProfileKey.kt
│ │ │ │ │ │ │ ├── ProfileFragment.kt
│ │ │ │ │ │ │ └── ProfileViewModel.kt
│ │ │ │ │ ├── AuthenticationManager.kt
│ │ │ │ │ ├── ServiceProvider.kt
│ │ │ │ │ └── Step7Activity.kt
│ │ │ │ ├── step_9
│ │ │ │ │ ├── utils
│ │ │ │ │ │ └── RxRelayUtils.kt
│ │ │ │ │ ├── core
│ │ │ │ │ │ ├── navigation
│ │ │ │ │ │ │ ├── NavUtils.kt
│ │ │ │ │ │ │ ├── BaseFragment.kt
│ │ │ │ │ │ │ ├── FragmentKey.kt
│ │ │ │ │ │ │ └── FragmentStateChanger.java
│ │ │ │ │ │ └── viewmodels
│ │ │ │ │ │ │ ├── HasServices.kt
│ │ │ │ │ │ │ └── ViewModelUtils.kt
│ │ │ │ │ ├── features
│ │ │ │ │ │ ├── registration
│ │ │ │ │ │ │ ├── EnterProfileDataKey.kt
│ │ │ │ │ │ │ ├── CreateLoginCredentialsKey.kt
│ │ │ │ │ │ │ ├── EnterProfileDataFragment.kt
│ │ │ │ │ │ │ ├── CreateLoginCredentialsFragment.kt
│ │ │ │ │ │ │ └── RegistrationViewModel.kt
│ │ │ │ │ │ ├── profile
│ │ │ │ │ │ │ ├── ProfileFragment.kt
│ │ │ │ │ │ │ ├── ProfileKey.kt
│ │ │ │ │ │ │ └── ProfileViewModel.kt
│ │ │ │ │ │ └── login
│ │ │ │ │ │ │ ├── LoginKey.kt
│ │ │ │ │ │ │ ├── LoginFragment.kt
│ │ │ │ │ │ │ └── LoginViewModel.kt
│ │ │ │ │ ├── AuthenticationManager.kt
│ │ │ │ │ ├── ServiceProvider.kt
│ │ │ │ │ └── Step9Activity.kt
│ │ │ │ ├── step_8
│ │ │ │ │ ├── core
│ │ │ │ │ │ ├── navigation
│ │ │ │ │ │ │ ├── NavUtils.kt
│ │ │ │ │ │ │ ├── BaseFragment.kt
│ │ │ │ │ │ │ ├── FragmentKey.kt
│ │ │ │ │ │ │ └── FragmentStateChanger.java
│ │ │ │ │ │ └── viewmodels
│ │ │ │ │ │ │ ├── HasServices.kt
│ │ │ │ │ │ │ ├── ServiceProvider.kt
│ │ │ │ │ │ │ └── ViewModelUtils.kt
│ │ │ │ │ ├── features
│ │ │ │ │ │ ├── main
│ │ │ │ │ │ │ ├── MainViewModel.kt
│ │ │ │ │ │ │ ├── MainFragment.kt
│ │ │ │ │ │ │ └── MainKey.kt
│ │ │ │ │ │ └── form
│ │ │ │ │ │ │ ├── FormKey.kt
│ │ │ │ │ │ │ ├── FormViewModel.kt
│ │ │ │ │ │ │ └── FormFragment.kt
│ │ │ │ │ └── Step8Activity.kt
│ │ │ │ ├── step_2
│ │ │ │ │ └── Step2Activity.kt
│ │ │ │ └── step_1
│ │ │ │ │ └── Step1Activity.kt
│ │ │ │ ├── utils
│ │ │ │ └── Utils.kt
│ │ │ │ └── app
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── zhuinden
│ │ │ └── simplestacktutorials
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── zhuinden
│ │ └── simplestacktutorials
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── encodings.xml
├── vcs.xml
├── runConfigurations.xml
├── gradle.xml
└── misc.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='SimpleStackTutorials'
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SimpleStackTutorials
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zhuinden/simple-stack-tutorials/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 15 00:57:59 CET 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3/Step3ButtonConfiguration.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_3
2 |
3 | import android.view.View
4 |
5 | data class Step3ButtonConfiguration(
6 | val buttonText: String,
7 | val buttonAction: (View) -> Unit
8 | )
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step4.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step5.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step6.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step7.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step8.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step9.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | abstract class Step5BaseFragment : Fragment() {
6 | fun getScreen(): T = requireArguments().getParcelable("FRAGMENT_KEY")!!
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | abstract class Step6BaseFragment : Fragment() {
6 | fun getScreen(): T = requireArguments().getParcelable("FRAGMENT_KEY")!!
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import android.view.View
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val View.backstack: Backstack
8 | get() = Navigator.getBackstack(context)
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_to_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_from_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_from_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/navigation/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.navigation
2 |
3 | import androidx.fragment.app.Fragment
4 |
5 | abstract class BaseFragment : Fragment() {
6 | fun getScreen(): T = requireArguments().getParcelable("FRAGMENT_KEY")!!
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val Fragment.backstack: Backstack
8 | get() = Navigator.getBackstack(requireContext())
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val Fragment.backstack: Backstack
8 | get() = Navigator.getBackstack(requireContext())
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3/Step3Screen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_3
2 |
3 | import android.os.Parcelable
4 |
5 | abstract class Step3Screen : Parcelable {
6 | abstract val titleText: String
7 |
8 | abstract val centerText: String
9 |
10 | abstract val buttonConfiguration: Step3ButtonConfiguration?
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/utils/RxRelayUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.utils
2 |
3 | import com.jakewharton.rxrelay2.BehaviorRelay
4 |
5 | fun BehaviorRelay.get(): T = value!!
6 |
7 | fun BehaviorRelay.getOrNull(): T? = value
8 |
9 | fun BehaviorRelay.set(value: T) {
10 | this.accept(value)
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/navigation/NavUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.navigation
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val Fragment.backstack: Backstack
8 | get() = Navigator.getBackstack(requireContext())
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/navigation/NavUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.navigation
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val Fragment.backstack: Backstack
8 | get() = Navigator.getBackstack(requireContext())
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/navigation/NavUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.navigation
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.navigator.Navigator
6 |
7 | val Fragment.backstack: Backstack
8 | get() = Navigator.getBackstack(requireContext())
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6FirstScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import androidx.fragment.app.Fragment
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step6FirstScreen(private val placeholder: String = "") : Step6Screen() {
8 | override fun instantiateFragment(): Fragment = Step6FirstFragment()
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6SecondScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import androidx.fragment.app.Fragment
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step6SecondScreen(private val placeholder: String = "") : Step6Screen() {
8 | override fun instantiateFragment(): Fragment = Step6SecondFragment()
9 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4FirstScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import com.zhuinden.simplestacktutorials.R
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step4FirstScreen(private val placeholder: String = "") : Step4Screen() { // generate equals/hashCode/toString
8 | override fun layout(): Int = R.layout.step4_first_view
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/viewmodels/HasServices.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels
2 |
3 | import com.zhuinden.simplestack.ScopeKey
4 | import com.zhuinden.simplestack.ServiceBinder
5 |
6 | interface HasServices: ScopeKey {
7 | fun bindServices(serviceBinder: ServiceBinder)
8 |
9 | override fun getScopeTag(): String = javaClass.name
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/viewmodels/HasServices.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels
2 |
3 | import com.zhuinden.simplestack.ScopeKey
4 | import com.zhuinden.simplestack.ServiceBinder
5 |
6 | interface HasServices: ScopeKey {
7 | fun bindServices(serviceBinder: ServiceBinder)
8 |
9 | override fun getScopeTag(): String = javaClass.name
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/navigation/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.navigation
2 |
3 | import androidx.annotation.LayoutRes
4 | import androidx.fragment.app.Fragment
5 |
6 | abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) {
7 | fun getScreen(): T = requireArguments().getParcelable("FRAGMENT_KEY")!!
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/viewmodels/HasServices.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels
2 |
3 | import com.zhuinden.simplestack.ScopeKey
4 | import com.zhuinden.simplestack.ServiceBinder
5 |
6 | interface HasServices : ScopeKey {
7 | fun bindServices(serviceBinder: ServiceBinder)
8 |
9 | override fun getScopeTag(): String = javaClass.name
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/navigation/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.navigation
2 |
3 | import androidx.annotation.LayoutRes
4 | import androidx.fragment.app.Fragment
5 |
6 | abstract class BaseFragment(@LayoutRes layoutRes: Int) : Fragment(layoutRes) {
7 | fun getScreen(): T = requireArguments().getParcelable("FRAGMENT_KEY")!!
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4SecondScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import com.zhuinden.simplestacktutorials.R
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step4SecondScreen(private val placeholder: String = "") :
8 | Step4Screen() { // generate equals/hashCode/toString
9 | override fun layout(): Int = R.layout.step4_second_view
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5FirstScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import androidx.fragment.app.Fragment
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step5FirstScreen(private val placeholder: String = "") :
8 | Step5Screen() { // generate equals/hashCode/toString
9 | override fun instantiateFragment(): Fragment = Step5FirstFragment()
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5SecondScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import androidx.fragment.app.Fragment
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class Step5SecondScreen(private val placeholder: String = "") :
8 | Step5Screen() { // generate equals/hashCode/toString
9 | override fun instantiateFragment(): Fragment = Step5SecondFragment()
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3/Step3SecondScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_3
2 |
3 | import kotlinx.android.parcel.Parcelize
4 |
5 | @Parcelize
6 | class Step3SecondScreen : Step3Screen() {
7 | override val titleText: String
8 | get() = "Second title"
9 | override val centerText: String
10 | get() = "Second screen"
11 | override val buttonConfiguration: Step3ButtonConfiguration?
12 | get() = null
13 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/zhuinden/simplestacktutorials/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4Screen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import android.os.Parcelable
4 | import com.zhuinden.simplestack.navigator.DefaultViewKey
5 | import com.zhuinden.simplestack.navigator.ViewChangeHandler
6 | import com.zhuinden.simplestack.navigator.changehandlers.SegueViewChangeHandler
7 |
8 | abstract class Step4Screen : DefaultViewKey, Parcelable {
9 | override fun viewChangeHandler(): ViewChangeHandler = SegueViewChangeHandler()
10 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5Screen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.fragment.app.Fragment
6 |
7 | abstract class Step5Screen : Parcelable {
8 | open val fragmentTag: String
9 | get() = toString()
10 |
11 | protected abstract fun instantiateFragment(): Fragment
12 |
13 | fun createFragment(): Fragment = instantiateFragment().apply {
14 | arguments = (arguments ?: Bundle()).also { bundle ->
15 | bundle.putParcelable("FRAGMENT_KEY", this@Step5Screen)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6Screen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.fragment.app.Fragment
6 |
7 | abstract class Step6Screen : Parcelable {
8 | open val fragmentTag: String
9 | get() = toString()
10 |
11 | protected abstract fun instantiateFragment(): Fragment
12 |
13 | fun createFragment(): Fragment = instantiateFragment().apply {
14 | arguments = (arguments ?: Bundle()).also { bundle ->
15 | bundle.putParcelable("FRAGMENT_KEY", this@Step6Screen)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3/Step3FirstScreen.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_3
2 |
3 | import com.zhuinden.simplestack.navigator.Navigator
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | class Step3FirstScreen : Step3Screen() {
8 | override val titleText: String
9 | get() = "First title"
10 | override val centerText: String
11 | get() = "First screen"
12 | override val buttonConfiguration: Step3ButtonConfiguration?
13 | get() = Step3ButtonConfiguration("Navigate forward") { view ->
14 | Navigator.getBackstack(view.context).goTo(Step3SecondScreen())
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/registration/EnterProfileDataKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.registration
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ScopeKey
5 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentKey
6 | import kotlinx.android.parcel.Parcelize
7 |
8 | @Parcelize
9 | data class EnterProfileDataKey(private val placeholder: String = ""): FragmentKey(), ScopeKey.Child {
10 | override fun instantiateFragment(): Fragment = EnterProfileDataFragment()
11 |
12 | override fun getParentScopes(): List = listOf("registration")
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/registration/EnterProfileDataKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.registration
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ScopeKey
5 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentKey
6 | import kotlinx.android.parcel.Parcelize
7 |
8 | @Parcelize
9 | data class EnterProfileDataKey(private val placeholder: String = "") : FragmentKey(), ScopeKey.Child {
10 | override fun instantiateFragment(): Fragment = EnterProfileDataFragment()
11 |
12 | override fun getParentScopes(): List = listOf("registration")
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/navigation/FragmentKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.navigation
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.fragment.app.Fragment
6 |
7 | abstract class FragmentKey: Parcelable {
8 | open val fragmentTag: String
9 | get() = toString()
10 |
11 | protected abstract fun instantiateFragment(): Fragment
12 |
13 | fun createFragment(): Fragment = instantiateFragment().apply {
14 | arguments = (arguments ?: Bundle()).also { bundle ->
15 | bundle.putParcelable("FRAGMENT_KEY", this@FragmentKey)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/navigation/FragmentKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.navigation
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.fragment.app.Fragment
6 |
7 | abstract class FragmentKey: Parcelable {
8 | open val fragmentTag: String
9 | get() = toString()
10 |
11 | protected abstract fun instantiateFragment(): Fragment
12 |
13 | fun createFragment(): Fragment = instantiateFragment().apply {
14 | arguments = (arguments ?: Bundle()).also { bundle ->
15 | bundle.putParcelable("FRAGMENT_KEY", this@FragmentKey)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/navigation/FragmentKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.navigation
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.fragment.app.Fragment
6 |
7 | abstract class FragmentKey : Parcelable {
8 | open val fragmentTag: String
9 | get() = toString()
10 |
11 | protected abstract fun instantiateFragment(): Fragment
12 |
13 | fun createFragment(): Fragment = instantiateFragment().apply {
14 | arguments = (arguments ?: Bundle()).also { bundle ->
15 | bundle.putParcelable("FRAGMENT_KEY", this@FragmentKey)
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/registration/CreateLoginCredentialsKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.registration
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ScopeKey
5 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentKey
6 | import kotlinx.android.parcel.Parcelize
7 |
8 | @Parcelize
9 | data class CreateLoginCredentialsKey(private val placeholder: String = ""): FragmentKey(), ScopeKey.Child {
10 | override fun instantiateFragment(): Fragment = CreateLoginCredentialsFragment()
11 |
12 | override fun getParentScopes(): List = listOf("registration")
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/profile/ProfileFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.profile
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import android.widget.Toast
6 | import com.zhuinden.simplestacktutorials.R
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.BaseFragment
8 |
9 | class ProfileFragment : BaseFragment(R.layout.step9_profile_fragment) {
10 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
11 | super.onViewCreated(view, savedInstanceState)
12 |
13 | Toast.makeText(requireContext(), "Welcome!", Toast.LENGTH_LONG).show()
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/registration/CreateLoginCredentialsKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.registration
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ScopeKey
5 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentKey
6 | import kotlinx.android.parcel.Parcelize
7 |
8 | @Parcelize
9 | data class CreateLoginCredentialsKey(private val placeholder: String = "") : FragmentKey(), ScopeKey.Child {
10 | override fun instantiateFragment(): Fragment = CreateLoginCredentialsFragment()
11 |
12 | override fun getParentScopes(): List = listOf("registration")
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tag_faces_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/viewmodels/ServiceProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels
2 |
3 | import com.zhuinden.simplestack.ScopedServices
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.FragmentKey
6 |
7 | class ServiceProvider : ScopedServices {
8 | override fun bindServices(serviceBinder: ServiceBinder) {
9 | val key = serviceBinder.getKey()
10 |
11 | val scope = serviceBinder.scopeTag
12 |
13 | if (key is HasServices && key.scopeTag == scope) {
14 | key.bindServices(serviceBinder) // screen-bound shared services
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6SecondFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.zhuinden.simplestacktutorials.R
8 |
9 | class Step6SecondFragment : Step6BaseFragment() {
10 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
11 | inflater.inflate(R.layout.step6_second_fragment, container, false)
12 |
13 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
14 | super.onViewCreated(view, savedInstanceState)
15 |
16 | val args = getScreen() // get args passed from previous screen
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/step6_second_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step6_first_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step7_profile_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step9_profile_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/zhuinden/simplestacktutorials/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.zhuinden.simplestacktutorials", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4FirstView.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.FrameLayout
6 | import com.zhuinden.simplestacktutorials.utils.onClick
7 | import kotlinx.android.synthetic.main.step4_first_view.view.*
8 |
9 | class Step4FirstView : FrameLayout {
10 | constructor(context: Context) : super(context)
11 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
12 | constructor(context: Context, attrs: AttributeSet?, defaultStyleRes: Int) : super(context, attrs, defaultStyleRes)
13 |
14 | override fun onFinishInflate() {
15 | super.onFinishInflate()
16 |
17 | step4FirstButton.onClick {
18 | backstack.goTo(Step4SecondScreen())
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.zhuinden.simplestack.History
6 | import com.zhuinden.simplestack.navigator.Navigator
7 | import com.zhuinden.simplestacktutorials.R
8 | import kotlinx.android.synthetic.main.activity_step4.*
9 |
10 | class Step4Activity : AppCompatActivity() {
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | setContentView(R.layout.activity_step4)
14 |
15 | Navigator.install(this, step4Root, History.of(Step4FirstScreen()))
16 | }
17 |
18 | override fun onBackPressed() {
19 | if (!Navigator.onBackPressed(this)) {
20 | super.onBackPressed()
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/main/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.main
2 |
3 | import com.zhuinden.simplestack.Bundleable
4 | import com.zhuinden.simplestacktutorials.steps.step_8.features.form.FormViewModel
5 | import com.zhuinden.statebundle.StateBundle
6 |
7 | class MainViewModel: FormViewModel.ResultHandler, Bundleable {
8 | var state: String = ""
9 | private set
10 |
11 | override fun handleResult(someData: String, moreData: String) {
12 | this.state = "$someData $moreData"
13 | }
14 |
15 | override fun toBundle(): StateBundle = StateBundle().apply {
16 | putString("state", state)
17 | }
18 |
19 | override fun fromBundle(bundle: StateBundle?) {
20 | bundle?.run {
21 | state = getString("state", "")
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/login/LoginKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.login
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.add
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | @Parcelize
11 | data class LoginKey(private val placeholder: String = ""): FragmentKey(), HasServices {
12 | override fun bindServices(serviceBinder: ServiceBinder) {
13 | with(serviceBinder) {
14 | add(LoginViewModel(lookupService("appContext"), backstack))
15 | }
16 | }
17 |
18 | override fun instantiateFragment(): Fragment = LoginFragment()
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/login/LoginKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.login
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.add
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | @Parcelize
11 | data class LoginKey(private val placeholder: String = "") : FragmentKey(), HasServices {
12 | override fun bindServices(serviceBinder: ServiceBinder) {
13 | with(serviceBinder) {
14 | add(LoginViewModel(lookupService("appContext"), backstack))
15 | }
16 | }
17 |
18 | override fun instantiateFragment(): Fragment = LoginFragment()
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/profile/ProfileKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.profile
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.add
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | @Parcelize
11 | data class ProfileKey(private val placeholder: String = ""): FragmentKey(), HasServices {
12 | override fun bindServices(serviceBinder: ServiceBinder) {
13 | with(serviceBinder) {
14 | add(ProfileViewModel(lookupService("appContext"), backstack))
15 | }
16 | }
17 |
18 | override fun instantiateFragment(): Fragment = ProfileFragment()
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/profile/ProfileKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.profile
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.add
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | @Parcelize
11 | data class ProfileKey(private val placeholder: String = "") : FragmentKey(), HasServices {
12 | override fun bindServices(serviceBinder: ServiceBinder) {
13 | with(serviceBinder) {
14 | add(ProfileViewModel(lookupService("appContext"), backstack))
15 | }
16 | }
17 |
18 | override fun instantiateFragment(): Fragment = ProfileFragment()
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/profile/ProfileFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.profile
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.Toast
8 | import com.zhuinden.simplestacktutorials.R
9 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.BaseFragment
10 |
11 | class ProfileFragment: BaseFragment() {
12 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
13 | inflater.inflate(R.layout.step7_profile_fragment, container, false)
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 |
18 | Toast.makeText(requireContext(), "Welcome!", Toast.LENGTH_LONG).show()
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5FirstFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.zhuinden.simplestacktutorials.R
8 | import com.zhuinden.simplestacktutorials.utils.onClick
9 | import kotlinx.android.synthetic.main.step5_first_fragment.*
10 |
11 | class Step5FirstFragment : Step5BaseFragment() {
12 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
13 | inflater.inflate(R.layout.step5_first_fragment, container, false)
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 |
18 | step5FirstButton.onClick {
19 | backstack.goTo(Step5SecondScreen())
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/profile/ProfileViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.profile
2 |
3 | import android.content.Context
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.History
6 | import com.zhuinden.simplestack.ScopedServices
7 | import com.zhuinden.simplestack.StateChange
8 | import com.zhuinden.simplestacktutorials.steps.step_9.AuthenticationManager
9 | import com.zhuinden.simplestacktutorials.steps.step_9.features.login.LoginKey
10 |
11 | class ProfileViewModel(
12 | private val appContext: Context,
13 | private val backstack: Backstack
14 | ) : ScopedServices.Activated {
15 | override fun onServiceActive() {
16 | if (!AuthenticationManager.isAuthenticated(appContext)) {
17 | backstack.setHistory(History.of(LoginKey()), StateChange.REPLACE)
18 | }
19 | }
20 |
21 | override fun onServiceInactive() {
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/form/FormKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.form
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.add
8 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.lookup
9 | import kotlinx.android.parcel.Parcelize
10 |
11 | @Parcelize
12 | data class FormKey(private val placeholder: String = ""): FragmentKey(), HasServices {
13 | override fun instantiateFragment(): Fragment = FormFragment()
14 |
15 | override fun bindServices(serviceBinder: ServiceBinder) {
16 | with(serviceBinder) {
17 | add(FormViewModel(lookup(), backstack))
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5SecondFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.zhuinden.simplestacktutorials.R
8 | import com.zhuinden.simplestacktutorials.utils.onClick
9 | import kotlinx.android.synthetic.main.step5_second_fragment.*
10 |
11 | class Step5SecondFragment : Step5BaseFragment() {
12 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
13 | inflater.inflate(R.layout.step5_second_fragment, container, false)
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 |
18 | val args = getScreen() // get args passed from previous screen
19 |
20 | step5SecondBack.onClick {
21 | backstack.goBack()
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4/Step4SecondView.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_4
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.FrameLayout
6 | import com.zhuinden.simplestack.Backstack
7 | import com.zhuinden.simplestacktutorials.utils.onClick
8 | import kotlinx.android.synthetic.main.step4_second_view.view.*
9 |
10 | class Step4SecondView : FrameLayout {
11 | constructor(context: Context) : super(context)
12 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
13 | constructor(context: Context, attrs: AttributeSet?, defaultStyleRes: Int) : super(context, attrs, defaultStyleRes)
14 |
15 | override fun onFinishInflate() {
16 | super.onFinishInflate()
17 |
18 | if (isInEditMode) {
19 | return
20 | }
21 |
22 | val args = Backstack.getKey(context) // get args passed from previous screen
23 |
24 | step4SecondBack.onClick {
25 | backstack.goBack()
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/profile/ProfileViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.profile
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.os.Looper
6 | import com.zhuinden.simplestack.Backstack
7 | import com.zhuinden.simplestack.History
8 | import com.zhuinden.simplestack.ScopedServices
9 | import com.zhuinden.simplestack.StateChange
10 | import com.zhuinden.simplestacktutorials.steps.step_7.AuthenticationManager
11 | import com.zhuinden.simplestacktutorials.steps.step_7.features.login.LoginKey
12 |
13 | class ProfileViewModel(
14 | private val appContext: Context,
15 | private val backstack: Backstack
16 | ) : ScopedServices.Activated {
17 | private val handler = Handler(Looper.getMainLooper())
18 |
19 | override fun onServiceActive() {
20 | if (!AuthenticationManager.isAuthenticated(appContext)) {
21 | backstack.setHistory(History.of(LoginKey()), StateChange.REPLACE)
22 | }
23 | }
24 |
25 | override fun onServiceInactive() {
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step1.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
20 |
21 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
21 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step8_main_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/form/FormViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.form
2 |
3 | import com.zhuinden.simplestack.Backstack
4 | import com.zhuinden.simplestack.Bundleable
5 | import com.zhuinden.statebundle.StateBundle
6 |
7 | class FormViewModel(
8 | private val resultHandler: ResultHandler,
9 | private val backstack: Backstack
10 | ): Bundleable {
11 | interface ResultHandler {
12 | fun handleResult(someData: String, moreData: String)
13 | }
14 |
15 | var someData: String = ""
16 | var moreData: String = ""
17 |
18 | fun onButtonClicked() {
19 | resultHandler.handleResult(someData, moreData)
20 | backstack.goBack()
21 | }
22 |
23 | override fun toBundle(): StateBundle = StateBundle().apply {
24 | putString("someData", someData)
25 | putString("moreData", moreData)
26 | }
27 |
28 | override fun fromBundle(bundle: StateBundle?) {
29 | bundle?.run {
30 | someData = getString("someData", "")
31 | moreData = getString("moreData", "")
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/main/MainFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.main
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.BaseFragment
7 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.backstack
8 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.lookup
9 | import com.zhuinden.simplestacktutorials.steps.step_8.features.form.FormKey
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import kotlinx.android.synthetic.main.step8_main_fragment.*
12 |
13 | class MainFragment: BaseFragment(R.layout.step8_main_fragment) {
14 | private val viewModel by lazy { lookup() }
15 |
16 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
17 | super.onViewCreated(view, savedInstanceState)
18 |
19 | textResult.setText(viewModel.state)
20 |
21 | buttonBeginFlow.onClick {
22 | backstack.goTo(FormKey())
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/AuthenticationManager.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7
2 |
3 | import android.content.Context
4 | import android.preference.PreferenceManager
5 |
6 | object AuthenticationManager {
7 | @Suppress("DEPRECATION") // w/e androidx-chan
8 | fun isAuthenticated(appContext: Context): Boolean {
9 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
10 | return sharedPref.getBoolean("isRegistered", false)
11 | }
12 |
13 | @Suppress("DEPRECATION") // w/e androidx-chan
14 | fun saveRegistration(appContext: Context) {
15 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
16 | sharedPref.edit().putBoolean("isRegistered", true).apply()
17 | }
18 |
19 | @Suppress("DEPRECATION") // w/e androidx-chan
20 | fun clearRegistration(appContext: Context) {
21 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
22 | sharedPref.edit().remove("isRegistered").apply()
23 | }
24 |
25 | var authToken: String = "" // why would this be in the viewModel?
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/AuthenticationManager.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9
2 |
3 | import android.content.Context
4 | import android.preference.PreferenceManager
5 |
6 | object AuthenticationManager {
7 | @Suppress("DEPRECATION") // w/e androidx-chan
8 | fun isAuthenticated(appContext: Context): Boolean {
9 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
10 | return sharedPref.getBoolean("isRegistered", false)
11 | }
12 |
13 | @Suppress("DEPRECATION") // w/e androidx-chan
14 | fun saveRegistration(appContext: Context) {
15 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
16 | sharedPref.edit().putBoolean("isRegistered", true).apply()
17 | }
18 |
19 | @Suppress("DEPRECATION") // w/e androidx-chan
20 | fun clearRegistration(appContext: Context) {
21 | val sharedPref = PreferenceManager.getDefaultSharedPreferences(appContext)
22 | sharedPref.edit().remove("isRegistered").apply()
23 | }
24 |
25 | var authToken: String = "" // why would this be in the viewModel?
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/step5_first_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/viewmodels/ViewModelUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.ServiceBinder
6 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.backstack
7 |
8 | inline fun Backstack.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
9 |
10 | inline fun Fragment.lookup(serviceTag: String = T::class.java.name) = backstack.lookup(serviceTag)
11 |
12 | inline fun ServiceBinder.add(service: T, serviceTag: String = T::class.java.name) {
13 | this.addService(serviceTag, service as Any)
14 | }
15 |
16 | inline fun ServiceBinder.bindAs(service: Any, serviceTag: String = NAME::class.java.name) {
17 | this.addAlias(serviceTag, service)
18 | }
19 |
20 | inline fun ServiceBinder.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
21 |
22 | inline fun ServiceBinder.get(serviceTag: String = T::class.java.name) = getService(serviceTag)
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/viewmodels/ViewModelUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.ServiceBinder
6 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.backstack
7 |
8 | inline fun Backstack.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
9 |
10 | inline fun Fragment.lookup(serviceTag: String = T::class.java.name) = backstack.lookupService(serviceTag)
11 |
12 | inline fun ServiceBinder.add(service: T, serviceTag: String = T::class.java.name) {
13 | this.addService(serviceTag, service as Any)
14 | }
15 |
16 | inline fun ServiceBinder.bindAs(service: Any, serviceTag: String = NAME::class.java.name) {
17 | this.addAlias(serviceTag, service)
18 | }
19 |
20 | inline fun ServiceBinder.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
21 |
22 | inline fun ServiceBinder.get(serviceTag: String = T::class.java.name) = getService(serviceTag)
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/main/MainKey.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.main
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.add
8 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.bindAs
9 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.get
10 | import com.zhuinden.simplestacktutorials.steps.step_8.features.form.FormViewModel
11 | import kotlinx.android.parcel.Parcelize
12 |
13 | @Parcelize
14 | data class MainKey(private val placeholder: String = ""): FragmentKey(), HasServices {
15 | override fun instantiateFragment(): Fragment = MainFragment()
16 |
17 | override fun bindServices(serviceBinder: ServiceBinder) {
18 | with(serviceBinder) {
19 | add(MainViewModel())
20 | bindAs(get())
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/viewmodels/ViewModelUtils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.zhuinden.simplestack.Backstack
5 | import com.zhuinden.simplestack.ServiceBinder
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.backstack
7 |
8 | inline fun Backstack.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
9 |
10 | inline fun Fragment.lookup(serviceTag: String = T::class.java.name) = backstack.lookupService(serviceTag)
11 |
12 | inline fun ServiceBinder.add(service: T, serviceTag: String = T::class.java.name) {
13 | this.addService(serviceTag, service as Any)
14 | }
15 |
16 | inline fun ServiceBinder.bindAs(service: Any, serviceTag: String = NAME::class.java.name) {
17 | this.addAlias(serviceTag, service)
18 | }
19 |
20 | inline fun ServiceBinder.lookup(serviceTag: String = T::class.java.name) = lookupService(serviceTag)
21 |
22 | inline fun ServiceBinder.get(serviceTag: String = T::class.java.name) = getService(serviceTag)
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/features/form/FormFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.features.form
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.BaseFragment
7 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.lookup
8 | import com.zhuinden.simplestacktutorials.utils.onClick
9 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
10 | import kotlinx.android.synthetic.main.step8_form_fragment.*
11 |
12 | class FormFragment: BaseFragment(R.layout.step8_form_fragment) {
13 | private val viewModel by lazy { lookup() }
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 |
18 | inputSomeData.setText(viewModel.someData)
19 | inputMoreData.setText(viewModel.moreData)
20 |
21 | inputSomeData.onTextChanged { viewModel.someData = it }
22 | inputMoreData.onTextChanged { viewModel.moreData = it }
23 |
24 | buttonPassResults.onClick { viewModel.onButtonClicked() }
25 | }
26 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=false
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step4_first_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step8_form_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/ServiceProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7
2 |
3 | import com.zhuinden.simplestack.ScopedServices
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.add
8 | import com.zhuinden.simplestacktutorials.steps.step_7.features.registration.RegistrationViewModel
9 |
10 | class ServiceProvider : ScopedServices {
11 | override fun bindServices(serviceBinder: ServiceBinder) {
12 | val key = serviceBinder.getKey()
13 |
14 | val scope = serviceBinder.scopeTag
15 |
16 | if (key is HasServices && key.scopeTag == scope) {
17 | key.bindServices(serviceBinder) // screen-bound shared services
18 | }
19 |
20 | with(serviceBinder) {
21 | when (scope) { // explicit shared services
22 | "registration" -> add(
23 | RegistrationViewModel(
24 | lookupService("appContext"),
25 | backstack
26 | )
27 | )
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/ServiceProvider.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9
2 |
3 | import com.zhuinden.simplestack.ScopedServices
4 | import com.zhuinden.simplestack.ServiceBinder
5 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentKey
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.HasServices
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.add
8 | import com.zhuinden.simplestacktutorials.steps.step_9.features.registration.RegistrationViewModel
9 |
10 | class ServiceProvider : ScopedServices {
11 | override fun bindServices(serviceBinder: ServiceBinder) {
12 | val key = serviceBinder.getKey()
13 |
14 | val scope = serviceBinder.scopeTag
15 |
16 | if (key is HasServices && key.scopeTag == scope) {
17 | key.bindServices(serviceBinder) // screen-bound shared services
18 | }
19 |
20 | with(serviceBinder) {
21 | when (scope) { // explicit shared services
22 | "registration" -> add(
23 | RegistrationViewModel(
24 | lookupService("appContext"),
25 | backstack
26 | )
27 | )
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6FirstFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import android.os.Bundle
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import com.zhuinden.simplestack.History
10 | import com.zhuinden.simplestack.StateChange
11 | import com.zhuinden.simplestacktutorials.R
12 |
13 | class Step6FirstFragment : Step6BaseFragment() {
14 | private val handler = Handler(Looper.getMainLooper())
15 |
16 | private var didNavigate = false
17 |
18 | private val runAfterDelay = Runnable {
19 | if (!didNavigate) {
20 | didNavigate = true
21 | backstack.setHistory(History.of(Step6SecondScreen()), StateChange.REPLACE) // <--
22 | }
23 | }
24 |
25 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
26 | inflater.inflate(R.layout.step6_first_fragment, container, false)
27 |
28 | override fun onStart() {
29 | super.onStart()
30 | handler.postDelayed(runAfterDelay, 1250L)
31 | }
32 |
33 | override fun onStop() {
34 | handler.removeCallbacksAndMessages(runAfterDelay)
35 | super.onStop()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/Step5Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.zhuinden.simplestack.History
6 | import com.zhuinden.simplestack.SimpleStateChanger
7 | import com.zhuinden.simplestack.StateChange
8 | import com.zhuinden.simplestack.navigator.Navigator
9 | import com.zhuinden.simplestacktutorials.R
10 | import kotlinx.android.synthetic.main.activity_step5.*
11 |
12 | class Step5Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
13 | private lateinit var fragmentStateChanger: FragmentStateChanger
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.activity_step5)
18 |
19 | fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.step5Root)
20 |
21 | Navigator.configure()
22 | .setStateChanger(SimpleStateChanger(this))
23 | .install(this, step5Root, History.of(Step5FirstScreen()))
24 | }
25 |
26 | override fun onBackPressed() {
27 | if (!Navigator.onBackPressed(this)) {
28 | super.onBackPressed()
29 | }
30 | }
31 |
32 | override fun onNavigationEvent(stateChange: StateChange) {
33 | fragmentStateChanger.handleStateChange(stateChange)
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/Step6Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.zhuinden.simplestack.History
6 | import com.zhuinden.simplestack.SimpleStateChanger
7 | import com.zhuinden.simplestack.StateChange
8 | import com.zhuinden.simplestack.navigator.Navigator
9 | import com.zhuinden.simplestacktutorials.R
10 | import kotlinx.android.synthetic.main.activity_step6.*
11 |
12 | class Step6Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
13 | private lateinit var fragmentStateChanger: FragmentStateChanger
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.activity_step6)
18 |
19 | fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.step6Root)
20 |
21 | Navigator.configure()
22 | .setStateChanger(SimpleStateChanger(this))
23 | .install(this, step6Root, History.of(Step6FirstScreen()))
24 | }
25 |
26 | override fun onBackPressed() {
27 | if (!Navigator.onBackPressed(this)) {
28 | super.onBackPressed()
29 | }
30 | }
31 |
32 | override fun onNavigationEvent(stateChange: StateChange) {
33 | fragmentStateChanger.handleStateChange(stateChange)
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/registration/EnterProfileDataFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.registration
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.zhuinden.simplestacktutorials.R
8 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.BaseFragment
9 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.lookup
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
12 | import kotlinx.android.synthetic.main.step7_enter_profile_data_fragment.*
13 |
14 | class EnterProfileDataFragment : BaseFragment() {
15 | private val viewModel by lazy { lookup() }
16 |
17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
18 | inflater.inflate(R.layout.step7_enter_profile_data_fragment, container, false)
19 |
20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21 | super.onViewCreated(view, savedInstanceState)
22 |
23 | textFullName.setText(viewModel.fullName)
24 | textBio.setText(viewModel.bio)
25 |
26 | textFullName.onTextChanged { fullName -> viewModel.onFullNameChanged(fullName) }
27 | textBio.onTextChanged { bio -> viewModel.onBioChanged(bio) }
28 | buttonEnterProfileNext.onClick { viewModel.onEnterProfileNextClicked() }
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/step5_second_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
22 |
26 |
27 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/registration/CreateLoginCredentialsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.registration
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import com.zhuinden.simplestacktutorials.R
8 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.BaseFragment
9 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.lookup
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
12 | import kotlinx.android.synthetic.main.step7_create_login_credentials_fragment.*
13 |
14 | class CreateLoginCredentialsFragment: BaseFragment() {
15 | private val viewModel by lazy { lookup() }
16 |
17 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
18 | inflater.inflate(R.layout.step7_create_login_credentials_fragment, container, false)
19 |
20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21 | super.onViewCreated(view, savedInstanceState)
22 |
23 | textUsername.setText(viewModel.username)
24 | textPassword.setText(viewModel.password)
25 |
26 | textUsername.onTextChanged { username -> viewModel.onUsernameChanged(username) }
27 | textPassword.onTextChanged { password -> viewModel.onPasswordChanged(password) }
28 | buttonRegisterAndLogin.onClick { viewModel.onRegisterAndLoginClicked() }
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/step7_enter_profile_data_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
18 |
19 |
26 |
27 |
33 |
34 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step9_enter_profile_data_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
18 |
19 |
26 |
27 |
33 |
34 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.utils
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.text.Editable
6 | import android.text.TextWatcher
7 | import android.view.View
8 | import android.widget.EditText
9 |
10 | fun View.onClick(clickListener: (View) -> Unit) {
11 | setOnClickListener(clickListener)
12 | }
13 |
14 | inline fun EditText.onTextChanged(crossinline textChangeListener: (String) -> Unit) {
15 | addTextChangedListener(object : TextWatcher {
16 | override fun afterTextChanged(editable: Editable) {
17 | textChangeListener(editable.toString())
18 | }
19 |
20 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
21 | }
22 |
23 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
24 | }
25 | })
26 | }
27 |
28 | inline fun Activity.startActivity() {
29 | startActivity(Intent(this, T::class.java))
30 | }
31 |
32 | fun Unit.safe() = Unit
33 |
34 | fun Any.safe() = Unit
35 |
36 | fun View.show() {
37 | this.visibility = View.VISIBLE
38 | }
39 |
40 | fun View.hide() {
41 | this.visibility = View.GONE
42 | }
43 |
44 | fun T.showIf(condition: (T) -> Boolean): T {
45 | if (condition(this)) {
46 | show()
47 | } else {
48 | hide()
49 | }
50 |
51 | return this
52 | }
53 |
54 | fun T.hideIf(condition: (T) -> Boolean): T {
55 | if (condition(this)) {
56 | hide()
57 | } else {
58 | show()
59 | }
60 |
61 | return this
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/Step8Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.zhuinden.simplestack.History
6 | import com.zhuinden.simplestack.SimpleStateChanger
7 | import com.zhuinden.simplestack.StateChange
8 | import com.zhuinden.simplestack.navigator.Navigator
9 | import com.zhuinden.simplestacktutorials.R
10 | import com.zhuinden.simplestacktutorials.steps.step_8.core.navigation.FragmentStateChanger
11 | import com.zhuinden.simplestacktutorials.steps.step_8.core.viewmodels.ServiceProvider
12 | import com.zhuinden.simplestacktutorials.steps.step_8.features.main.MainKey
13 |
14 | class Step8Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
15 | private lateinit var fragmentStateChanger: FragmentStateChanger
16 |
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | setContentView(R.layout.activity_step8)
20 |
21 | fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.step8Root)
22 |
23 | Navigator.configure()
24 | .setStateChanger(SimpleStateChanger(this))
25 | .setScopedServices(ServiceProvider())
26 | .install(this, findViewById(R.id.step8Root), History.of(MainKey()))
27 | }
28 |
29 | override fun onBackPressed() {
30 | if (!Navigator.onBackPressed(this)) {
31 | super.onBackPressed()
32 | }
33 | }
34 |
35 | override fun onNavigationEvent(stateChange: StateChange) {
36 | fragmentStateChanger.handleStateChange(stateChange)
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/step4_second_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
22 |
26 |
27 |
34 |
35 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step7_create_login_credentials_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
18 |
19 |
26 |
27 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step9_create_login_credentials_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
18 |
19 |
26 |
27 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 | defaultConfig {
9 | applicationId "com.zhuinden.simplestacktutorials"
10 | minSdkVersion 21
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0"
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | }
23 |
24 | dependencies {
25 | implementation fileTree(dir: 'libs', include: ['*.jar'])
26 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
27 | implementation 'androidx.appcompat:appcompat:1.1.0'
28 | implementation 'androidx.core:core-ktx:1.2.0'
29 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
30 | implementation "androidx.recyclerview:recyclerview:1.1.0"
31 | implementation "com.xwray:groupie:2.7.0"
32 | implementation "com.xwray:groupie-kotlin-android-extensions:2.7.0"
33 |
34 | implementation 'com.github.Zhuinden:simple-stack:2.3.2'
35 | implementation 'com.github.Zhuinden:event-emitter:1.1.0'
36 | implementation 'com.github.Zhuinden:rx-combinetuple-kt:1.1.0'
37 |
38 | implementation 'io.reactivex.rxjava2:rxkotlin:2.4.0'
39 | implementation 'com.jakewharton.rxrelay2:rxrelay:2.1.1'
40 |
41 | testImplementation 'junit:junit:4.12'
42 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step7_login_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
29 |
30 |
37 |
38 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/step9_login_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
29 |
30 |
37 |
38 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/login/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.login
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.BaseFragment
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.lookup
8 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.get
9 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.set
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
12 | import io.reactivex.disposables.CompositeDisposable
13 | import io.reactivex.rxkotlin.addTo
14 | import io.reactivex.rxkotlin.subscribeBy
15 | import kotlinx.android.synthetic.main.step9_login_fragment.*
16 |
17 | class LoginFragment : BaseFragment(R.layout.step9_login_fragment) {
18 | private val viewModel by lazy { lookup() }
19 |
20 | private val compositeDisposable = CompositeDisposable()
21 |
22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23 | super.onViewCreated(view, savedInstanceState)
24 |
25 | textUsername.setText(viewModel.username.get())
26 | textPassword.setText(viewModel.password.get())
27 |
28 | viewModel.isLoginEnabled.distinctUntilChanged().subscribeBy { enabled ->
29 | buttonLogin.isEnabled = enabled
30 | }.addTo(compositeDisposable)
31 |
32 | textUsername.onTextChanged { username -> viewModel.username.set(username) }
33 | textPassword.onTextChanged { password -> viewModel.password.set(password) }
34 | buttonLogin.onClick { viewModel.onLoginClicked() }
35 | buttonRegister.onClick { viewModel.onRegisterClicked() }
36 | }
37 |
38 | override fun onDestroyView() {
39 | compositeDisposable.clear()
40 | super.onDestroyView()
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/registration/EnterProfileDataFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.registration
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.BaseFragment
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.lookup
8 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.get
9 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.set
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
12 | import io.reactivex.disposables.CompositeDisposable
13 | import io.reactivex.rxkotlin.addTo
14 | import io.reactivex.rxkotlin.subscribeBy
15 | import kotlinx.android.synthetic.main.step9_enter_profile_data_fragment.*
16 |
17 | class EnterProfileDataFragment : BaseFragment(R.layout.step9_enter_profile_data_fragment) {
18 | private val viewModel by lazy { lookup() }
19 |
20 | private val compositeDisposable = CompositeDisposable()
21 |
22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23 | super.onViewCreated(view, savedInstanceState)
24 |
25 | textFullName.setText(viewModel.fullName.get())
26 | textBio.setText(viewModel.bio.get())
27 |
28 | viewModel.isEnterProfileNextEnabled.distinctUntilChanged().subscribeBy { enabled ->
29 | buttonEnterProfileNext.isEnabled = enabled
30 | }.addTo(compositeDisposable)
31 |
32 | textFullName.onTextChanged { fullName -> viewModel.fullName.set(fullName) }
33 | textBio.onTextChanged { bio -> viewModel.bio.set(bio) }
34 | buttonEnterProfileNext.onClick { viewModel.onEnterProfileNextClicked() }
35 | }
36 |
37 | override fun onDestroyView() {
38 | super.onDestroyView()
39 | compositeDisposable.clear()
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/registration/CreateLoginCredentialsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.registration
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.BaseFragment
7 | import com.zhuinden.simplestacktutorials.steps.step_9.core.viewmodels.lookup
8 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.get
9 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.set
10 | import com.zhuinden.simplestacktutorials.utils.onClick
11 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
12 | import io.reactivex.disposables.CompositeDisposable
13 | import io.reactivex.rxkotlin.addTo
14 | import io.reactivex.rxkotlin.subscribeBy
15 | import kotlinx.android.synthetic.main.step9_create_login_credentials_fragment.*
16 |
17 | class CreateLoginCredentialsFragment : BaseFragment(R.layout.step9_create_login_credentials_fragment) {
18 | private val viewModel by lazy { lookup() }
19 |
20 | private val compositeDisposable = CompositeDisposable()
21 |
22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23 | super.onViewCreated(view, savedInstanceState)
24 |
25 | textUsername.setText(viewModel.username.get())
26 | textPassword.setText(viewModel.password.get())
27 |
28 | viewModel.isRegisterAndLoginEnabled.distinctUntilChanged().subscribeBy { enabled ->
29 | buttonRegisterAndLogin.isEnabled = enabled
30 | }.addTo(compositeDisposable)
31 |
32 | textUsername.onTextChanged { username -> viewModel.username.set(username) }
33 | textPassword.onTextChanged { password -> viewModel.password.set(password) }
34 | buttonRegisterAndLogin.onClick { viewModel.onRegisterAndLoginClicked() }
35 | }
36 |
37 | override fun onDestroyView() {
38 | super.onDestroyView()
39 | compositeDisposable.clear()
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.app
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.zhuinden.simplestacktutorials.R
6 | import com.zhuinden.simplestacktutorials.steps.step_1.Step1Activity
7 | import com.zhuinden.simplestacktutorials.steps.step_2.Step2Activity
8 | import com.zhuinden.simplestacktutorials.steps.step_3.Step3Activity
9 | import com.zhuinden.simplestacktutorials.steps.step_4.Step4Activity
10 | import com.zhuinden.simplestacktutorials.steps.step_5.Step5Activity
11 | import com.zhuinden.simplestacktutorials.steps.step_6.Step6Activity
12 | import com.zhuinden.simplestacktutorials.steps.step_7.Step7Activity
13 | import com.zhuinden.simplestacktutorials.steps.step_8.Step8Activity
14 | import com.zhuinden.simplestacktutorials.steps.step_9.Step9Activity
15 | import com.zhuinden.simplestacktutorials.utils.onClick
16 | import com.zhuinden.simplestacktutorials.utils.startActivity
17 | import kotlinx.android.synthetic.main.activity_main.*
18 |
19 | class MainActivity : AppCompatActivity() {
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_main)
23 |
24 | buttonStep1.onClick {
25 | startActivity()
26 | }
27 |
28 | buttonStep2.onClick {
29 | startActivity()
30 | }
31 |
32 | buttonStep3.onClick {
33 | startActivity()
34 | }
35 |
36 | buttonStep4.onClick {
37 | startActivity()
38 | }
39 |
40 | buttonStep5.onClick {
41 | startActivity()
42 | }
43 |
44 | buttonStep6.onClick {
45 | startActivity()
46 | }
47 |
48 | buttonStep7.onClick {
49 | startActivity()
50 | }
51 |
52 | buttonStep8.onClick {
53 | startActivity()
54 | }
55 |
56 | buttonStep9.onClick {
57 | startActivity()
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/login/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.login
2 |
3 | import android.content.Context
4 | import com.zhuinden.eventemitter.EventEmitter
5 | import com.zhuinden.eventemitter.EventSource
6 | import com.zhuinden.simplestack.Backstack
7 | import com.zhuinden.simplestack.Bundleable
8 | import com.zhuinden.simplestack.History
9 | import com.zhuinden.simplestack.StateChange
10 | import com.zhuinden.simplestacktutorials.steps.step_7.AuthenticationManager
11 | import com.zhuinden.simplestacktutorials.steps.step_7.features.profile.ProfileKey
12 | import com.zhuinden.simplestacktutorials.steps.step_7.features.registration.EnterProfileDataKey
13 | import com.zhuinden.statebundle.StateBundle
14 |
15 | class LoginViewModel(
16 | private val appContext: Context,
17 | private val backstack: Backstack
18 | ) : Bundleable {
19 | private val eventEmitter: EventEmitter = EventEmitter()
20 | val events: EventSource get() = eventEmitter
21 |
22 | var username: String = ""
23 | private set
24 | var password: String = ""
25 | private set
26 |
27 | fun onUsernameChanged(username: String) {
28 | this.username = username
29 | }
30 |
31 | fun onPasswordChanged(password: String) {
32 | this.password = password
33 | }
34 |
35 | fun onLoginClicked() {
36 | if (username.isNotBlank() && password.isNotBlank()) {
37 | AuthenticationManager.saveRegistration(appContext)
38 | backstack.setHistory(History.of(ProfileKey()), StateChange.FORWARD)
39 | } else {
40 | eventEmitter.emit("Invalid username or password!")
41 | }
42 | }
43 |
44 | fun onRegisterClicked() {
45 | backstack.goTo(EnterProfileDataKey())
46 | }
47 |
48 | override fun toBundle(): StateBundle = StateBundle().apply {
49 | putString("username", username)
50 | putString("password", password)
51 | }
52 |
53 | override fun fromBundle(bundle: StateBundle?) {
54 | bundle?.run {
55 | username = getString("username", "")
56 | password = getString("password", "")
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/login/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.login
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.Toast
8 | import com.zhuinden.eventemitter.EventSource
9 | import com.zhuinden.simplestacktutorials.R
10 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.BaseFragment
11 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.backstack
12 | import com.zhuinden.simplestacktutorials.steps.step_7.core.viewmodels.lookup
13 | import com.zhuinden.simplestacktutorials.utils.onClick
14 | import com.zhuinden.simplestacktutorials.utils.onTextChanged
15 | import kotlinx.android.synthetic.main.step7_login_fragment.*
16 |
17 | class LoginFragment : BaseFragment() {
18 | private val viewModel by lazy { backstack.lookup() }
19 |
20 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
21 | inflater.inflate(R.layout.step7_login_fragment, container, false)
22 |
23 | private var subscription: EventSource.NotificationToken? = null
24 |
25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
26 | super.onViewCreated(view, savedInstanceState)
27 |
28 | textUsername.setText(viewModel.username)
29 | textPassword.setText(viewModel.password)
30 |
31 | textUsername.onTextChanged { username -> viewModel.onUsernameChanged(username) }
32 | textPassword.onTextChanged { password -> viewModel.onPasswordChanged(password) }
33 | buttonLogin.onClick { viewModel.onLoginClicked() }
34 | buttonRegister.onClick { viewModel.onRegisterClicked() }
35 | }
36 |
37 | override fun onStart() {
38 | super.onStart()
39 |
40 | subscription = viewModel.events.startListening { message ->
41 | Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
42 | }
43 | }
44 |
45 | override fun onStop() {
46 | subscription?.stopListening()
47 | subscription = null
48 |
49 | super.onStop()
50 | }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3/Step3Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_3
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.zhuinden.simplestack.Backstack
7 | import com.zhuinden.simplestack.History
8 | import com.zhuinden.simplestack.SimpleStateChanger
9 | import com.zhuinden.simplestack.StateChange
10 | import com.zhuinden.simplestack.navigator.Navigator
11 | import com.zhuinden.simplestacktutorials.R
12 | import com.zhuinden.simplestacktutorials.utils.hide
13 | import com.zhuinden.simplestacktutorials.utils.onClick
14 | import com.zhuinden.simplestacktutorials.utils.show
15 | import com.zhuinden.simplestacktutorials.utils.showIf
16 | import kotlinx.android.synthetic.main.activity_step3.*
17 |
18 | private val Activity.backstack: Backstack
19 | get() = Navigator.getBackstack(this)
20 |
21 | class Step3Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | setContentView(R.layout.activity_step3)
25 |
26 | step3TitleButtonBack.onClick {
27 | backstack.goBack()
28 | }
29 |
30 | Navigator.configure()
31 | .setStateChanger(SimpleStateChanger(this))
32 | .install(this, step3Root, History.of(Step3FirstScreen()))
33 | }
34 |
35 | override fun onBackPressed() {
36 | if (!Navigator.onBackPressed(this)) {
37 | super.onBackPressed()
38 | }
39 | }
40 |
41 | override fun onNavigationEvent(stateChange: StateChange) {
42 | val newKeys = stateChange.getNewKeys()
43 | step3TitleButtonBack.showIf { newKeys.size > 1 } // show up if can go back
44 |
45 | val topKey = stateChange.topNewKey()
46 |
47 | step3TitleText.text = topKey.titleText
48 | step3CenterText.text = topKey.centerText
49 |
50 | val buttonConfiguration = topKey.buttonConfiguration
51 | if (buttonConfiguration == null) {
52 | step3Button.hide()
53 | } else {
54 | step3Button.show()
55 | step3Button.text = buttonConfiguration.buttonText
56 | step3Button.onClick(buttonConfiguration.buttonAction)
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_2/Step2Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_2
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.os.Parcelable
6 | import androidx.appcompat.app.AppCompatActivity
7 | import com.zhuinden.simplestack.Backstack
8 | import com.zhuinden.simplestack.History
9 | import com.zhuinden.simplestack.SimpleStateChanger
10 | import com.zhuinden.simplestack.StateChange
11 | import com.zhuinden.simplestack.navigator.Navigator
12 | import com.zhuinden.simplestacktutorials.R
13 | import com.zhuinden.simplestacktutorials.utils.hide
14 | import com.zhuinden.simplestacktutorials.utils.onClick
15 | import com.zhuinden.simplestacktutorials.utils.safe
16 | import com.zhuinden.simplestacktutorials.utils.show
17 | import kotlinx.android.parcel.Parcelize
18 | import kotlinx.android.synthetic.main.activity_step2.*
19 |
20 | private val Context.backstack: Backstack
21 | get() = Navigator.getBackstack(this)
22 |
23 | class Step2Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
24 | sealed class Screens : Parcelable {
25 | @Parcelize
26 | object First : Screens()
27 |
28 | @Parcelize
29 | object Second : Screens()
30 | }
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | setContentView(R.layout.activity_step2)
35 |
36 | Navigator.configure()
37 | .setStateChanger(SimpleStateChanger(this))
38 | .install(this, step2Root, History.of(Screens.First)) // auto-install backstack
39 | }
40 |
41 | override fun onBackPressed() {
42 | if (!Navigator.onBackPressed(this)) {
43 | super.onBackPressed()
44 | }
45 | }
46 |
47 | override fun onNavigationEvent(stateChange: StateChange) {
48 | val newKey = stateChange.topNewKey()
49 |
50 | when (newKey) {
51 | Screens.First -> {
52 | step2Text.text = "First Screen"
53 |
54 | step2Button.show()
55 | step2Button.onClick {
56 | backstack.goTo(Screens.Second)
57 | }
58 | }
59 | Screens.Second -> {
60 | step2Text.text = "Second Screen"
61 |
62 | step2Button.hide()
63 | }
64 | }.safe()
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/login/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.login
2 |
3 | import android.content.Context
4 | import com.jakewharton.rxrelay2.BehaviorRelay
5 | import com.zhuinden.rxcombinetuplekt.combineTuple
6 | import com.zhuinden.simplestack.*
7 | import com.zhuinden.simplestacktutorials.steps.step_9.AuthenticationManager
8 | import com.zhuinden.simplestacktutorials.steps.step_9.features.profile.ProfileKey
9 | import com.zhuinden.simplestacktutorials.steps.step_9.features.registration.EnterProfileDataKey
10 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.get
11 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.set
12 | import com.zhuinden.statebundle.StateBundle
13 | import io.reactivex.disposables.CompositeDisposable
14 | import io.reactivex.rxkotlin.addTo
15 | import io.reactivex.rxkotlin.subscribeBy
16 |
17 | class LoginViewModel(
18 | private val appContext: Context,
19 | private val backstack: Backstack
20 | ) : Bundleable, ScopedServices.Registered {
21 | private val compositeDisposable = CompositeDisposable()
22 |
23 | val username = BehaviorRelay.createDefault("")
24 | val password = BehaviorRelay.createDefault("")
25 |
26 | val isLoginEnabled = BehaviorRelay.createDefault(false)
27 |
28 | override fun onServiceRegistered() {
29 | combineTuple(username, password)
30 | .subscribeBy { (username, password) ->
31 | isLoginEnabled.set(username.isNotBlank() && password.isNotBlank())
32 | }.addTo(compositeDisposable)
33 | }
34 |
35 | override fun onServiceUnregistered() {
36 | compositeDisposable.clear()
37 | }
38 |
39 | fun onLoginClicked() {
40 | if (isLoginEnabled.get()) {
41 | AuthenticationManager.saveRegistration(appContext)
42 | backstack.setHistory(History.of(ProfileKey()), StateChange.FORWARD)
43 | }
44 | }
45 |
46 | fun onRegisterClicked() {
47 | backstack.goTo(EnterProfileDataKey())
48 | }
49 |
50 | override fun toBundle(): StateBundle = StateBundle().apply {
51 | putString("username", username.get())
52 | putString("password", password.get())
53 | }
54 |
55 | override fun fromBundle(bundle: StateBundle?) {
56 | bundle?.run {
57 | username.set(getString("username", ""))
58 | password.set(getString("password", ""))
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/Step7Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.zhuinden.simplestack.GlobalServices
7 | import com.zhuinden.simplestack.History
8 | import com.zhuinden.simplestack.SimpleStateChanger
9 | import com.zhuinden.simplestack.StateChange
10 | import com.zhuinden.simplestack.navigator.Navigator
11 | import com.zhuinden.simplestacktutorials.R
12 | import com.zhuinden.simplestacktutorials.steps.step_7.core.navigation.FragmentStateChanger
13 | import com.zhuinden.simplestacktutorials.steps.step_7.features.login.LoginKey
14 | import com.zhuinden.simplestacktutorials.steps.step_7.features.profile.ProfileKey
15 | import kotlinx.android.synthetic.main.activity_step7.*
16 |
17 | class Step7Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
18 | private lateinit var fragmentStateChanger: FragmentStateChanger
19 | private lateinit var appContext: Context
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_step7)
24 |
25 | fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.step7Root)
26 | appContext = applicationContext
27 |
28 | Navigator.configure()
29 | .setStateChanger(SimpleStateChanger(this))
30 | .setScopedServices(ServiceProvider())
31 | .setGlobalServices(
32 | GlobalServices.builder()
33 | .addService("appContext", appContext)
34 | .build()
35 | )
36 | .install(
37 | this, step7Root, History.of(
38 | when {
39 | AuthenticationManager.isAuthenticated(appContext) -> ProfileKey()
40 | else -> LoginKey()
41 | }
42 | )
43 | )
44 | }
45 |
46 | override fun onBackPressed() {
47 | if (!Navigator.onBackPressed(this)) {
48 | super.onBackPressed()
49 | }
50 | }
51 |
52 | override fun onNavigationEvent(stateChange: StateChange) {
53 | fragmentStateChanger.handleStateChange(stateChange)
54 | }
55 |
56 | override fun onDestroy() {
57 | if (isFinishing) {
58 | AuthenticationManager.clearRegistration(appContext) // just for sample repeat sake
59 | }
60 |
61 | super.onDestroy()
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/Step9Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.zhuinden.simplestack.GlobalServices
7 | import com.zhuinden.simplestack.History
8 | import com.zhuinden.simplestack.SimpleStateChanger
9 | import com.zhuinden.simplestack.StateChange
10 | import com.zhuinden.simplestack.navigator.Navigator
11 | import com.zhuinden.simplestacktutorials.R
12 | import com.zhuinden.simplestacktutorials.steps.step_9.core.navigation.FragmentStateChanger
13 | import com.zhuinden.simplestacktutorials.steps.step_9.features.login.LoginKey
14 | import com.zhuinden.simplestacktutorials.steps.step_9.features.profile.ProfileKey
15 | import kotlinx.android.synthetic.main.activity_step9.*
16 |
17 | class Step9Activity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
18 | private lateinit var fragmentStateChanger: FragmentStateChanger
19 | private lateinit var appContext: Context
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_step9)
24 |
25 | fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.step9Root)
26 | appContext = applicationContext
27 |
28 | Navigator.configure()
29 | .setStateChanger(SimpleStateChanger(this))
30 | .setScopedServices(ServiceProvider())
31 | .setGlobalServices(
32 | GlobalServices.builder()
33 | .addService("appContext", appContext)
34 | .build()
35 | )
36 | .install(
37 | this, step9Root, History.of(
38 | when {
39 | AuthenticationManager.isAuthenticated(appContext) -> ProfileKey()
40 | else -> LoginKey()
41 | }
42 | )
43 | )
44 | }
45 |
46 | override fun onBackPressed() {
47 | if (!Navigator.onBackPressed(this)) {
48 | super.onBackPressed()
49 | }
50 | }
51 |
52 | override fun onNavigationEvent(stateChange: StateChange) {
53 | fragmentStateChanger.handleStateChange(stateChange)
54 | }
55 |
56 | override fun onDestroy() {
57 | if (isFinishing) {
58 | AuthenticationManager.clearRegistration(appContext) // just for sample repeat sake
59 | }
60 |
61 | super.onDestroy()
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_step3.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
28 |
29 |
35 |
36 |
37 |
38 |
46 |
47 |
54 |
55 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/features/registration/RegistrationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.features.registration
2 |
3 | import android.content.Context
4 | import com.zhuinden.simplestack.*
5 | import com.zhuinden.simplestacktutorials.steps.step_7.AuthenticationManager
6 | import com.zhuinden.simplestacktutorials.steps.step_7.features.profile.ProfileKey
7 | import com.zhuinden.statebundle.StateBundle
8 |
9 | class RegistrationViewModel(
10 | private val appContext: Context,
11 | private val backstack: Backstack
12 | ): Bundleable, ScopedServices.HandlesBack {
13 | enum class RegistrationState { // this is actually kinda superfluous/unnecessary but ok
14 | COLLECT_PROFILE_DATA,
15 | COLLECT_USER_PASSWORD,
16 | REGISTRATION_COMPLETED
17 | }
18 |
19 | private var currentState: RegistrationState = RegistrationState.COLLECT_PROFILE_DATA
20 |
21 | var fullName: String = ""
22 | private set
23 | var bio: String = ""
24 | private set
25 |
26 | var username: String = ""
27 | private set
28 | var password: String = ""
29 | private set
30 |
31 | fun onFullNameChanged(fullName: String) {
32 | this.fullName = fullName
33 | }
34 |
35 | fun onBioChanged(bio: String) {
36 | this.bio = bio
37 | }
38 |
39 | fun onUsernameChanged(username: String) {
40 | this.username = username
41 | }
42 |
43 | fun onPasswordChanged(password: String) {
44 | this.password = password
45 | }
46 |
47 | fun onRegisterAndLoginClicked() {
48 | if(username.isNotBlank() && password.isNotBlank()) {
49 | currentState = RegistrationState.REGISTRATION_COMPLETED
50 | AuthenticationManager.saveRegistration(appContext)
51 | backstack.setHistory(History.of(ProfileKey()), StateChange.FORWARD)
52 | }
53 | }
54 |
55 | fun onEnterProfileNextClicked() {
56 | if(fullName.isNotBlank() && bio.isNotBlank()) {
57 | currentState = RegistrationState.COLLECT_USER_PASSWORD
58 | backstack.goTo(CreateLoginCredentialsKey())
59 | }
60 | }
61 |
62 | override fun onBackEvent(): Boolean {
63 | if(currentState == RegistrationState.COLLECT_USER_PASSWORD) {
64 | currentState = RegistrationState.COLLECT_PROFILE_DATA
65 | return false // already dispatching, so just go back a screen
66 | }
67 | return false
68 | }
69 |
70 | override fun toBundle(): StateBundle = StateBundle().apply {
71 | putSerializable("currentState", currentState)
72 | putString("username", username)
73 | putString("password", password)
74 | putString("fullName", fullName)
75 | putString("bio", bio)
76 | }
77 |
78 | override fun fromBundle(bundle: StateBundle?) {
79 | bundle?.run {
80 | currentState = getSerializable("currentState") as RegistrationState
81 | username = getString("username", "")
82 | password = getString("password", "")
83 | fullName = getString("fullName", "")
84 | bio = getString("bio", bio)
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5/FragmentStateChanger.java:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_5;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import com.zhuinden.simplestack.StateChange;
8 | import com.zhuinden.simplestacktutorials.R;
9 |
10 | import java.util.List;
11 |
12 | public class FragmentStateChanger {
13 | private FragmentManager fragmentManager;
14 | private int containerId;
15 |
16 | public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
17 | this.fragmentManager = fragmentManager;
18 | this.containerId = containerId;
19 | }
20 |
21 | public void handleStateChange(StateChange stateChange) {
22 | fragmentManager.executePendingTransactions();
23 |
24 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().disallowAddToBackStack();
25 | if (stateChange.getDirection() == StateChange.FORWARD) {
26 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_right, R.anim.slide_out_to_left);
27 | } else if (stateChange.getDirection() == StateChange.BACKWARD) {
28 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right, R.anim.slide_in_from_left, R.anim.slide_out_to_right);
29 | }
30 |
31 | List previousKeys = stateChange.getPreviousKeys();
32 | List newKeys = stateChange.getNewKeys();
33 | for (Step5Screen oldKey : previousKeys) {
34 | Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
35 | if (fragment != null) {
36 | if (!newKeys.contains(oldKey)) {
37 | fragmentTransaction.remove(fragment); // remove fragments not in backstack
38 | } else if (!fragment.isDetached()) {
39 | fragmentTransaction.detach(fragment); // destroy view of fragment not top
40 | }
41 | }
42 | }
43 | for (Step5Screen newKey : newKeys) {
44 | Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
45 | if (newKey.equals(stateChange.topNewKey())) {
46 | if (fragment != null) {
47 | if (fragment.isRemoving()) { // fragments are quirky, they die asynchronously. Ignore if they're still there.
48 | fragmentTransaction.replace(containerId, newKey.createFragment(), newKey.getFragmentTag());
49 | } else if (fragment.isDetached()) {
50 | fragmentTransaction.attach(fragment); // show top fragment if already exists
51 | }
52 | } else {
53 | fragment = newKey.createFragment(); // create and add new top if did not exist
54 | fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
55 | }
56 | } else {
57 | if (fragment != null && !fragment.isDetached()) {
58 | fragmentTransaction.detach(fragment); // fragment not on top should not be showing
59 | }
60 | }
61 | }
62 | fragmentTransaction.commitAllowingStateLoss();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6/FragmentStateChanger.java:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_6;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import com.zhuinden.simplestack.StateChange;
8 | import com.zhuinden.simplestacktutorials.R;
9 |
10 | import java.util.List;
11 |
12 | public class FragmentStateChanger {
13 | private FragmentManager fragmentManager;
14 | private int containerId;
15 |
16 | public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
17 | this.fragmentManager = fragmentManager;
18 | this.containerId = containerId;
19 | }
20 |
21 | public void handleStateChange(StateChange stateChange) {
22 | fragmentManager.executePendingTransactions();
23 |
24 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().disallowAddToBackStack();
25 | if (stateChange.getDirection() == StateChange.FORWARD) {
26 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_right, R.anim.slide_out_to_left);
27 | } else if (stateChange.getDirection() == StateChange.BACKWARD) {
28 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right, R.anim.slide_in_from_left, R.anim.slide_out_to_right);
29 | } else { // REPLACE
30 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
31 | }
32 |
33 | List previousKeys = stateChange.getPreviousKeys();
34 | List newKeys = stateChange.getNewKeys();
35 | for (Step6Screen oldKey : previousKeys) {
36 | Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
37 | if (fragment != null) {
38 | if (!newKeys.contains(oldKey)) {
39 | fragmentTransaction.remove(fragment); // remove fragments not in backstack
40 | } else if (!fragment.isDetached()) {
41 | fragmentTransaction.detach(fragment); // destroy view of fragment not top
42 | }
43 | }
44 | }
45 | for (Step6Screen newKey : newKeys) {
46 | Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
47 | if (newKey.equals(stateChange.topNewKey())) {
48 | if (fragment != null) {
49 | if (fragment.isRemoving()) { // fragments are quirky, they die asynchronously. Ignore if they're still there.
50 | fragmentTransaction.replace(containerId, newKey.createFragment(), newKey.getFragmentTag());
51 | } else if (fragment.isDetached()) {
52 | fragmentTransaction.attach(fragment); // show top fragment if already exists
53 | }
54 | } else {
55 | fragment = newKey.createFragment(); // create and add new top if did not exist
56 | fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
57 | }
58 | } else {
59 | if (fragment != null && !fragment.isDetached()) {
60 | fragmentTransaction.detach(fragment); // fragment not on top should not be showing
61 | }
62 | }
63 | }
64 | fragmentTransaction.commitAllowingStateLoss();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
28 |
29 |
35 |
36 |
42 |
43 |
49 |
50 |
56 |
57 |
63 |
64 |
70 |
71 |
77 |
78 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7/core/navigation/FragmentStateChanger.java:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_7.core.navigation;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import com.zhuinden.simplestack.StateChange;
8 | import com.zhuinden.simplestacktutorials.R;
9 |
10 | import java.util.List;
11 |
12 | public class FragmentStateChanger {
13 | private FragmentManager fragmentManager;
14 | private int containerId;
15 |
16 | public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
17 | this.fragmentManager = fragmentManager;
18 | this.containerId = containerId;
19 | }
20 |
21 | public void handleStateChange(StateChange stateChange) {
22 | fragmentManager.executePendingTransactions(); // two synchronous immediate fragment transactions can overlap.
23 |
24 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().disallowAddToBackStack();
25 | if (stateChange.getDirection() == StateChange.FORWARD) {
26 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_right, R.anim.slide_out_to_left);
27 | } else if (stateChange.getDirection() == StateChange.BACKWARD) {
28 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right, R.anim.slide_in_from_left, R.anim.slide_out_to_right);
29 | } else { // REPLACE
30 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
31 | }
32 |
33 | List previousKeys = stateChange.getPreviousKeys();
34 | List newKeys = stateChange.getNewKeys();
35 | for (FragmentKey oldKey : previousKeys) {
36 | Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
37 | if (fragment != null) {
38 | if (!newKeys.contains(oldKey)) {
39 | fragmentTransaction.remove(fragment); // remove fragments not in backstack
40 | } else if (!fragment.isDetached()) {
41 | fragmentTransaction.detach(fragment); // destroy view of fragment not top
42 | }
43 | }
44 | }
45 | for (FragmentKey newKey : newKeys) {
46 | Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
47 | if (newKey.equals(stateChange.topNewKey())) {
48 | if (fragment != null) {
49 | if (fragment.isRemoving()) { // fragments are quirky, they die asynchronously. Ignore if they're still there.
50 | fragmentTransaction.replace(containerId, newKey.createFragment(), newKey.getFragmentTag());
51 | } else if (fragment.isDetached()) {
52 | fragmentTransaction.attach(fragment); // show top fragment if already exists
53 | }
54 | } else {
55 | fragment = newKey.createFragment(); // create and add new top if did not exist
56 | fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
57 | }
58 | } else {
59 | if (fragment != null && !fragment.isDetached()) {
60 | fragmentTransaction.detach(fragment); // fragment not on top should not be showing
61 | }
62 | }
63 | }
64 | fragmentTransaction.commitAllowingStateLoss();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8/core/navigation/FragmentStateChanger.java:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_8.core.navigation;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import com.zhuinden.simplestack.StateChange;
8 | import com.zhuinden.simplestacktutorials.R;
9 |
10 | import java.util.List;
11 |
12 | public class FragmentStateChanger {
13 | private FragmentManager fragmentManager;
14 | private int containerId;
15 |
16 | public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
17 | this.fragmentManager = fragmentManager;
18 | this.containerId = containerId;
19 | }
20 |
21 | public void handleStateChange(StateChange stateChange) {
22 | fragmentManager.executePendingTransactions(); // two synchronous immediate fragment transactions can overlap.
23 |
24 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().disallowAddToBackStack();
25 | if (stateChange.getDirection() == StateChange.FORWARD) {
26 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_right, R.anim.slide_out_to_left);
27 | } else if (stateChange.getDirection() == StateChange.BACKWARD) {
28 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right, R.anim.slide_in_from_left, R.anim.slide_out_to_right);
29 | } else { // REPLACE
30 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
31 | }
32 |
33 | List previousKeys = stateChange.getPreviousKeys();
34 | List newKeys = stateChange.getNewKeys();
35 | for (FragmentKey oldKey : previousKeys) {
36 | Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
37 | if (fragment != null) {
38 | if (!newKeys.contains(oldKey)) {
39 | fragmentTransaction.remove(fragment); // remove fragments not in backstack
40 | } else if (!fragment.isDetached()) {
41 | fragmentTransaction.detach(fragment); // destroy view of fragment not top
42 | }
43 | }
44 | }
45 | for (FragmentKey newKey : newKeys) {
46 | Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
47 | if (newKey.equals(stateChange.topNewKey())) {
48 | if (fragment != null) {
49 | if (fragment.isRemoving()) { // fragments are quirky, they die asynchronously. Ignore if they're still there.
50 | fragmentTransaction.replace(containerId, newKey.createFragment(), newKey.getFragmentTag());
51 | } else if (fragment.isDetached()) {
52 | fragmentTransaction.attach(fragment); // show top fragment if already exists
53 | }
54 | } else {
55 | fragment = newKey.createFragment(); // create and add new top if did not exist
56 | fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
57 | }
58 | } else {
59 | if (fragment != null && !fragment.isDetached()) {
60 | fragmentTransaction.detach(fragment); // fragment not on top should not be showing
61 | }
62 | }
63 | }
64 | fragmentTransaction.commitAllowingStateLoss();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/core/navigation/FragmentStateChanger.java:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.core.navigation;
2 |
3 | import androidx.fragment.app.Fragment;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.fragment.app.FragmentTransaction;
6 |
7 | import com.zhuinden.simplestack.StateChange;
8 | import com.zhuinden.simplestacktutorials.R;
9 |
10 | import java.util.List;
11 |
12 | public class FragmentStateChanger {
13 | private FragmentManager fragmentManager;
14 | private int containerId;
15 |
16 | public FragmentStateChanger(FragmentManager fragmentManager, int containerId) {
17 | this.fragmentManager = fragmentManager;
18 | this.containerId = containerId;
19 | }
20 |
21 | public void handleStateChange(StateChange stateChange) {
22 | fragmentManager.executePendingTransactions(); // two synchronous immediate fragment transactions can overlap.
23 |
24 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().disallowAddToBackStack();
25 | if (stateChange.getDirection() == StateChange.FORWARD) {
26 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_right, R.anim.slide_out_to_left, R.anim.slide_in_from_right, R.anim.slide_out_to_left);
27 | } else if (stateChange.getDirection() == StateChange.BACKWARD) {
28 | fragmentTransaction.setCustomAnimations(R.anim.slide_in_from_left, R.anim.slide_out_to_right, R.anim.slide_in_from_left, R.anim.slide_out_to_right);
29 | } else { // REPLACE
30 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
31 | }
32 |
33 | List previousKeys = stateChange.getPreviousKeys();
34 | List newKeys = stateChange.getNewKeys();
35 | for (FragmentKey oldKey : previousKeys) {
36 | Fragment fragment = fragmentManager.findFragmentByTag(oldKey.getFragmentTag());
37 | if (fragment != null) {
38 | if (!newKeys.contains(oldKey)) {
39 | fragmentTransaction.remove(fragment); // remove fragments not in backstack
40 | } else if (!fragment.isDetached()) {
41 | fragmentTransaction.detach(fragment); // destroy view of fragment not top
42 | }
43 | }
44 | }
45 | for (FragmentKey newKey : newKeys) {
46 | Fragment fragment = fragmentManager.findFragmentByTag(newKey.getFragmentTag());
47 | if (newKey.equals(stateChange.topNewKey())) {
48 | if (fragment != null) {
49 | if (fragment.isRemoving()) { // fragments are quirky, they die asynchronously. Ignore if they're still there.
50 | fragmentTransaction.replace(containerId, newKey.createFragment(), newKey.getFragmentTag());
51 | } else if (fragment.isDetached()) {
52 | fragmentTransaction.attach(fragment); // show top fragment if already exists
53 | }
54 | } else {
55 | fragment = newKey.createFragment(); // create and add new top if did not exist
56 | fragmentTransaction.add(containerId, fragment, newKey.getFragmentTag());
57 | }
58 | } else {
59 | if (fragment != null && !fragment.isDetached()) {
60 | fragmentTransaction.detach(fragment); // fragment not on top should not be showing
61 | }
62 | }
63 | }
64 | fragmentTransaction.commitAllowingStateLoss();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_1/Step1Activity.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_1
2 |
3 | import android.os.Bundle
4 | import android.os.Parcelable
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.zhuinden.simplestack.Backstack
7 | import com.zhuinden.simplestack.History
8 | import com.zhuinden.simplestack.StateChange
9 | import com.zhuinden.simplestack.StateChanger
10 | import com.zhuinden.simplestacktutorials.R
11 | import com.zhuinden.simplestacktutorials.utils.hide
12 | import com.zhuinden.simplestacktutorials.utils.onClick
13 | import com.zhuinden.simplestacktutorials.utils.safe
14 | import com.zhuinden.simplestacktutorials.utils.show
15 | import kotlinx.android.parcel.Parcelize
16 | import kotlinx.android.synthetic.main.activity_step1.*
17 |
18 | class Step1Activity : AppCompatActivity(), StateChanger {
19 | private lateinit var backstack: Backstack
20 |
21 | sealed class Screens : Parcelable { // a screen should be Parcelable so they can be put to Bundle.
22 | @Parcelize
23 | object First : Screens()
24 |
25 | @Parcelize
26 | object Second : Screens()
27 | }
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContentView(R.layout.activity_step1)
32 |
33 | @Suppress("DEPRECATION") // don't worry, Navigator will handle it in step 2
34 | backstack = lastCustomNonConfigurationInstance?.let { it as Backstack } ?: Backstack().also { backstack ->
35 | backstack.setup(History.of(Screens.First))
36 |
37 | savedInstanceState?.let { bundle ->
38 | backstack.fromBundle(bundle.getParcelable("BACKSTACK_STATE"))
39 | }
40 | }
41 |
42 | backstack.setStateChanger(this) // handle navigation in this class
43 | }
44 |
45 | override fun onBackPressed() {
46 | if (!backstack.goBack()) {
47 | super.onBackPressed()
48 | }
49 | }
50 |
51 | override fun handleStateChange(stateChange: StateChange, completionCallback: StateChanger.Callback) {
52 | if (stateChange.isTopNewKeyEqualToPrevious) { // handle when you navigate to the same screen twice
53 | completionCallback.stateChangeComplete() // don't do anything in this case
54 | return
55 | }
56 |
57 | val newKey = stateChange.topNewKey()
58 |
59 | when (newKey) {
60 | Screens.First -> {
61 | step1Text.text = "First Screen"
62 |
63 | step1Button.show()
64 | step1Button.onClick {
65 | backstack.goTo(Screens.Second)
66 | }
67 | }
68 | Screens.Second -> {
69 | step1Text.text = "Second Screen"
70 |
71 | step1Button.hide()
72 | }
73 | }.safe()
74 |
75 | completionCallback.stateChangeComplete()
76 | }
77 |
78 | // some android-specific plumbing code, Navigator hides this in step 2
79 | override fun onRetainCustomNonConfigurationInstance(): Any? = backstack
80 |
81 | override fun onSaveInstanceState(outState: Bundle) {
82 | super.onSaveInstanceState(outState)
83 | outState.putParcelable("BACKSTACK_STATE", backstack.toBundle())
84 | }
85 |
86 | override fun onResume() {
87 | super.onResume()
88 | backstack.reattachStateChanger()
89 | }
90 |
91 | override fun onPause() {
92 | backstack.detachStateChanger()
93 | super.onPause()
94 | }
95 |
96 | override fun onDestroy() {
97 | backstack.executePendingStateChange()
98 |
99 | if (isFinishing) {
100 | backstack.finalizeScopes()
101 | }
102 |
103 | super.onDestroy()
104 | }
105 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9/features/registration/RegistrationViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.zhuinden.simplestacktutorials.steps.step_9.features.registration
2 |
3 | import android.content.Context
4 | import com.jakewharton.rxrelay2.BehaviorRelay
5 | import com.zhuinden.rxcombinetuplekt.combineTuple
6 | import com.zhuinden.simplestack.*
7 | import com.zhuinden.simplestacktutorials.steps.step_9.AuthenticationManager
8 | import com.zhuinden.simplestacktutorials.steps.step_9.features.profile.ProfileKey
9 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.get
10 | import com.zhuinden.simplestacktutorials.steps.step_9.utils.set
11 | import com.zhuinden.statebundle.StateBundle
12 | import io.reactivex.disposables.CompositeDisposable
13 | import io.reactivex.rxkotlin.addTo
14 | import io.reactivex.rxkotlin.subscribeBy
15 |
16 | class RegistrationViewModel(
17 | private val appContext: Context,
18 | private val backstack: Backstack
19 | ) : Bundleable, ScopedServices.Registered, ScopedServices.HandlesBack {
20 | enum class RegistrationState { // this is actually kinda superfluous/unnecessary but ok
21 | COLLECT_PROFILE_DATA,
22 | COLLECT_USER_PASSWORD,
23 | REGISTRATION_COMPLETED
24 | }
25 |
26 | private var currentState: RegistrationState = RegistrationState.COLLECT_PROFILE_DATA
27 |
28 | private val compositeDisposable = CompositeDisposable()
29 |
30 | val fullName = BehaviorRelay.createDefault("")
31 | val bio = BehaviorRelay.createDefault("")
32 |
33 | val username = BehaviorRelay.createDefault("")
34 | val password = BehaviorRelay.createDefault("")
35 |
36 | val isRegisterAndLoginEnabled = BehaviorRelay.createDefault(false)
37 | val isEnterProfileNextEnabled = BehaviorRelay.createDefault(false)
38 |
39 | override fun onServiceRegistered() {
40 | combineTuple(fullName, bio)
41 | .subscribeBy { (fullName, bio) ->
42 | isEnterProfileNextEnabled.set(fullName.isNotBlank() && bio.isNotBlank())
43 | }.addTo(compositeDisposable)
44 |
45 | combineTuple(username, password)
46 | .subscribeBy { (username, password) ->
47 | isRegisterAndLoginEnabled.set(username.isNotBlank() && password.isNotBlank())
48 | }.addTo(compositeDisposable)
49 | }
50 |
51 | override fun onServiceUnregistered() {
52 | compositeDisposable.clear()
53 | }
54 |
55 | fun onRegisterAndLoginClicked() {
56 | if (isRegisterAndLoginEnabled.get()) {
57 | currentState = RegistrationState.REGISTRATION_COMPLETED
58 | AuthenticationManager.saveRegistration(appContext)
59 | backstack.setHistory(History.of(ProfileKey()), StateChange.FORWARD)
60 | }
61 | }
62 |
63 | fun onEnterProfileNextClicked() {
64 | if (isEnterProfileNextEnabled.get()) {
65 | currentState = RegistrationState.COLLECT_USER_PASSWORD
66 | backstack.goTo(CreateLoginCredentialsKey())
67 | }
68 | }
69 |
70 | override fun onBackEvent(): Boolean {
71 | if (currentState == RegistrationState.COLLECT_USER_PASSWORD) {
72 | currentState = RegistrationState.COLLECT_PROFILE_DATA
73 | return false // already dispatching, so just go back a screen
74 | }
75 | return false
76 | }
77 |
78 | override fun toBundle(): StateBundle = StateBundle().apply {
79 | putSerializable("currentState", currentState)
80 | putString("username", username.get())
81 | putString("password", password.get())
82 | putString("fullName", fullName.get())
83 | putString("bio", bio.get())
84 | }
85 |
86 | override fun fromBundle(bundle: StateBundle?) {
87 | bundle?.run {
88 | currentState = getSerializable("currentState") as RegistrationState
89 | username.set(getString("username", ""))
90 | password.set(getString("password", ""))
91 | fullName.set(getString("fullName", ""))
92 | bio.set(getString("bio", ""))
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | xmlns:android
18 |
19 | ^$
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:.*
29 |
30 | ^$
31 |
32 |
33 | BY_NAME
34 |
35 |
36 |
37 |
38 |
39 |
40 | .*:id
41 |
42 | http://schemas.android.com/apk/res/android
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:name
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | name
63 |
64 | ^$
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | style
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | .*
85 |
86 | ^$
87 |
88 |
89 | BY_NAME
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | http://schemas.android.com/apk/res/android
99 |
100 |
101 | ANDROID_ATTRIBUTE_ORDER
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | .*
111 |
112 |
113 | BY_NAME
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tutorials for Simple-Stack
2 |
3 | This repository contains tutorials for the [`simple-stack` navigation library]([Simple-Stack](https://github.com/Zhuinden/simple-stack)) for Android.
4 |
5 | ## [1.) Using `Backstack` directly](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_1)
6 |
7 | In this step, we create a `Backstack` in our Activity to track our navigation state.
8 |
9 | Then, we forward the necessary callbacks to make it work, even if there's a meteor storm or equivalent in Android's ecosystem.
10 |
11 | We see that we can pass in any Parcelable and can navigate between them seamlessly, and handle a change between them in the `handleStateChange` callback.
12 |
13 | Please note that this is the rawest form of simple-stack and will be greatly improved in step 2.
14 |
15 | ## [2.) Using `Navigator` to hide the lifecycle callbacks](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_2)
16 |
17 | In this step, we replace `Backstack` with `Navigator`, to see how much we can simplify the installation of a Backstack - down to just `onCreate` and `onBackPressed`.
18 |
19 | ## [3.) Setting the title text based on our navigation history](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_3)
20 |
21 | In this step, we can see how easy it is to set up any arbitrary state based on our current navigation history.
22 |
23 | The example shows how to show the "back" button when there is an available screen to go back to, and how to change the title text accordingly.
24 |
25 | ## [4.) Using custom views instead of handling navigation state directly in the Activity](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_4)
26 |
27 | In this step, we can see how to use custom views (compound viewgroups) to create self-contained components that can contain their own views and manage their own behavior.
28 |
29 | ## [5.) Using fragments instead of custom views because that's also possible](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_5)
30 |
31 | In this step, we replace custom views (compound viewgroups) with Fragments, because they're more commonly found in the wild.
32 |
33 | We can see that the Fragment framework (thanks to `attach`/`detach`/`add`/`remove`) is customizable enough that we can keep our Fragments exactly in the state as we expect them to be in based on our current navigation history.
34 |
35 | ## [6.) Using `setHistory()` to implement "conditional navigation"](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_6)
36 |
37 | In this step, we can see how to implement a simple splash screen using `backstack.setHistory()`.
38 |
39 | ## [7.) Implementing "First Time User Experience" with simple-stack](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_7)
40 |
41 | In this step, we can see an example of implementing a "first time user experience" flow, following the behavior outlined in the guide ["Jetpack Navigation: Conditional Navigation - First Time User Experience"](https://developer.android.com/guide/navigation/navigation-conditional#first-time_user_experience).
42 |
43 | We also utilize the power of global services bound to the global scope, and a shared ViewModel bound to both shared identifier across screens (`registration`), and ViewModels bound to the scope of a given screen (`ScopeKey` / `HasServices`).
44 |
45 | With this, we implement a FTUE where the user's navigation state and inputs are properly preserved across both configuration changes AND [process death](https://youtu.be/sLCn27DceRA?t=1231) (as per [Core App Quality Guidelines FN-S2](https://developer.android.com/docs/quality-guidelines/core-app-quality#fn)).
46 |
47 | ## [8.) Result passing between scoped services](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_8)
48 |
49 | In this step, we can see how to use scoped services to pass results across screens.
50 |
51 | By using `serviceBinder.lookupService()`, we can retrieve a service in the parent scope registered by a name (for example, the class name of the interface that handles the results).
52 |
53 | This way, we can handle result passing between screens as a regular callback method.
54 |
55 | ## [9.) Adding RxJava and RxRelay to "First Time User Experience"](https://github.com/Zhuinden/simple-stack-tutorials/tree/93554f7000efe49fca39de7ca707eb6843a5eaf8/app/src/main/java/com/zhuinden/simplestacktutorials/steps/step_9)
56 |
57 | In this step, we'll add RxJava's BehaviorRelays and create ViewModel-internal subscriptions to provide field validation.
58 |
59 | ## License
60 |
61 | Copyright 2020 Gabor Varadi
62 |
63 | Licensed under the Apache License, Version 2.0 (the "License");
64 | you may not use this file except in compliance with the License.
65 | You may obtain a copy of the License at
66 |
67 | http://www.apache.org/licenses/LICENSE-2.0
68 |
69 | Unless required by applicable law or agreed to in writing, software
70 | distributed under the License is distributed on an "AS IS" BASIS,
71 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
72 | See the License for the specific language governing permissions and
73 | limitations under the License.
74 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------