Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e1436a223 | |||
| bd9ac36efc | |||
| 2dc6fca41c | |||
| e210e45c36 | |||
| f1a5881a9e | |||
| af396eb974 | |||
| b37a6c979d | |||
| 701feee394 | |||
| b399dd3173 | |||
| 2a4969d756 | |||
| 8521b7e19d | |||
| 8b2eab847b | |||
| cbaabaf0bc | |||
| 192e35ca71 | |||
| ff77162654 | |||
| 82256a9785 | |||
| bbd27daee3 | |||
| 4a54fc1dd4 | |||
| 4fc5b26980 | |||
| daef586461 | |||
| 7a862cb061 | |||
| f2de564600 | |||
| 4c674c2583 | |||
| 22182bddb8 | |||
| ae9f4aea70 | |||
| edfc5b5300 | |||
| ee2dd68f09 | |||
| c3873c6e9d | |||
| f44410fcfe | |||
| 608a32c3be | |||
| 17428f0189 | |||
| 1701bc4f36 | |||
| 9c9c56bc9b | |||
| 3da8fe52ca | |||
| a943aab08e | |||
| 86ac421fb8 | |||
| f7009f53e8 | |||
| 994c497147 | |||
| bb13ace1c9 | |||
| dbe065275d | |||
| 2ebbc98a99 | |||
| ba7eea60f5 | |||
| 6347fe7d95 | |||
| 2b6e2ee874 | |||
| f50a4d8106 | |||
| 3535f491f5 | |||
| 1c5b89d2ec | |||
| 127cce1fe4 | |||
| 3bea311400 | |||
| a0b3dfa71e | |||
| 0e9ff3f7f5 | |||
| 004faf77c4 | |||
| 214a6eddf1 | |||
| a4c94003ad | |||
| 4f7c1c1607 | |||
| 16f8fd833e | |||
| 77c3ecc76e | |||
| 062073cb37 | |||
| b9cddc290e |
+4
-1
@@ -10,4 +10,7 @@ local.properties
|
||||
**/*.iml
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
# GPG keys
|
||||
*.gpg
|
||||
@@ -1,33 +1,57 @@
|
||||
# Cicerone
|
||||
[](https://bintray.com/terrakok/terramaven/cicerone/_latestVersion)
|
||||
[](https://repo1.maven.org/maven2/com/github/terrakok/cicerone/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
[](https://android-arsenal.com/details/1/4700)
|
||||
[](http://androidweekly.net/issues/issue-250)
|
||||
[](http://androidweekly.net/issues/issue-271)
|
||||
|
||||

|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://github.com/terrakok/Cicerone/raw/master/media/navigation.gif" width="256"/>
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://github.com/terrakok/Cicerone/raw/master/media/insta_tabs.gif" width="256"/>
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://github.com/terrakok/Cicerone/raw/master/media/animations.gif" width="256"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Power navigation
|
||||
</td>
|
||||
<td>
|
||||
Multibackstack
|
||||
</td>
|
||||
<td>
|
||||
Result listeners
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Cicerone (a guide, one who conducts sightseers) is a lightweight library that makes the navigation in an Android app easy.
|
||||
Cicerone (a guide who gives information about antiquities and places of interest to sightseers) is a lightweight library that makes the navigation in an Android app easy.
|
||||
It was designed to be used with the MVP/MVVM/MVI patterns but will work great with any architecture.
|
||||
|
||||
## Main advantages
|
||||
+ is not tied to Fragments
|
||||
+ not a framework
|
||||
+ short navigation calls (no builders)
|
||||
+ lifecycle-safe!
|
||||
+ functionality is simple to extend
|
||||
+ suitable for Unit Testing
|
||||
+ Is not tied to Fragments
|
||||
+ Not a framework (very lightweight)
|
||||
+ Short navigation calls (no builders)
|
||||
+ Static typed checks for screen parameters!
|
||||
+ Lifecycle-safe!
|
||||
+ Functionality is simple to extend
|
||||
+ Suitable for Unit Testing
|
||||
|
||||
## Additional features
|
||||
+ opening several screens inside single call (for example: deeplink)
|
||||
+ provides `FragmentFactory` if it needed
|
||||
+ Opening several screens inside single call (for example: deeplink)
|
||||
+ Provides `FragmentFactory` if it needed
|
||||
+ `add` or `replace` strategy for opening next screen (see `router.navigateTo` last parameter)
|
||||
+ implementation of parallel navigation (Instagram like)
|
||||
+ predefined navigator ready for Single-Activity apps
|
||||
+ predefined navigator ready for setup transition animation
|
||||
+ Implementation of parallel navigation (Instagram like)
|
||||
+ Predefined navigator ready for Single-Activity apps
|
||||
+ Predefined navigator ready for setup transition animation
|
||||
|
||||
## How to add
|
||||
## How to add Cicerone to your application
|
||||
Add the dependency in your build.gradle:
|
||||
```kotlin
|
||||
dependencies {
|
||||
@@ -55,10 +79,10 @@ class App : Application() {
|
||||
}
|
||||
```
|
||||
|
||||
## How it works?
|
||||
<img src="https://github.com/terrakok/Cicerone/raw/develop/media/CiceroneDiagram.png" alt="drawing" width="800"/>
|
||||
## How does it work?
|
||||
<img src="https://github.com/terrakok/Cicerone/blob/master/media/CiceroneDiagram.png" alt="CiceroneDiagram.png" width="800"/>
|
||||
|
||||
Presenter calls navigation method of Router.
|
||||
The `Presenter` calls the navigation method of `Router`.
|
||||
|
||||
```kotlin
|
||||
class SamplePresenter(
|
||||
@@ -75,11 +99,11 @@ class SamplePresenter(
|
||||
}
|
||||
```
|
||||
|
||||
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
|
||||
`Router` converts the navigation call to the set of commands and sends them to `CommandBuffer`.
|
||||
|
||||
CommandBuffer checks whether there are _"active"_ Navigator:
|
||||
- If yes, it passes the commands to the Navigator. Navigator will process them to achive the desired transition.
|
||||
- If no, then CommandBuffer saves the commands in a queue, and will apply them as soon as new _"active"_ Navigator will appear.
|
||||
`CommandBuffer` checks whether there are the `_"active"_ Navigator`:
|
||||
- If yes, it passes the commands to the Navigator. `Navigator` will process them to achive the desired transition.
|
||||
- If no, then `CommandBuffer` saves the commands in a queue, and will apply them as soon as a new `_"active"_ Navigator` will appear.
|
||||
|
||||
```kotlin
|
||||
fun executeCommands(commands: Array<out Command>) {
|
||||
@@ -87,10 +111,10 @@ fun executeCommands(commands: Array<out Command>) {
|
||||
}
|
||||
```
|
||||
|
||||
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
|
||||
Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.
|
||||
`Navigator` processes the navigation commands. Usually it is an anonymous class inside `Activity`.
|
||||
`Activity` provides `Navigator` to the `CommandBuffer` in `_onResume_` and removes it in `_onPause_`.
|
||||
|
||||
**Attention**: Use _onResumeFragments()_ with FragmentActivity ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
|
||||
**Attention**: Use `_onResumeFragments()_` with `FragmentActivity` ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
|
||||
|
||||
```kotlin
|
||||
private val navigator = AppNavigator(this, R.id.container)
|
||||
@@ -107,15 +131,15 @@ override fun onPause() {
|
||||
```
|
||||
|
||||
## Navigation commands
|
||||
This commands set will fulfill the needs of the most applications. But if you need something special - just add it!
|
||||
These commands will fulfill the needs of the most applications. But if you need something special - just add it!
|
||||
+ Forward - Opens new screen
|
||||

|
||||

|
||||
+ Back - Rolls back the last transition
|
||||

|
||||

|
||||
+ BackTo - Rolls back to the needed screen in the screens chain
|
||||

|
||||

|
||||
+ Replace - Replaces the current screen
|
||||

|
||||

|
||||
|
||||
## Predefined navigator
|
||||
The library provides predefined navigator for _Fragments_ and _Activity_.
|
||||
@@ -124,16 +148,16 @@ To use, just provide it with the container and _FragmentManager_.
|
||||
private val navigator = AppNavigator(this, R.id.container)
|
||||
```
|
||||
|
||||
Custom navigator can be useful sometimes:
|
||||
A custom navigator can be useful sometimes:
|
||||
```kotlin
|
||||
private val navigator = object : AppNavigator(this, R.id.container) {
|
||||
override fun setupFragmentTransaction(
|
||||
screen: FragmentScreen,
|
||||
fragmentTransaction: FragmentTransaction,
|
||||
currentFragment: Fragment?,
|
||||
nextFragment: Fragment?
|
||||
nextFragment: Fragment
|
||||
) {
|
||||
super.setupFragmentTransaction(fragmentTransaction, currentFragment, nextFragment)
|
||||
fragmentTransaction.setReorderingAllowed(true)
|
||||
//setup your animation
|
||||
}
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
@@ -144,31 +168,68 @@ private val navigator = object : AppNavigator(this, R.id.container) {
|
||||
```
|
||||
|
||||
## Screens
|
||||
Describe your screens as you like e.g. create Kotlin `object` with all application screens:
|
||||
Describe your screens as you like e.g. create a Kotlin `object` with all application screens:
|
||||
```kotlin
|
||||
object Screens {
|
||||
val Main = FragmentScreen("MainFragment") { MainFragment() }
|
||||
val AddressSearch = FragmentScreen("AddressSearchFragment") { AddressSearchFragment() }
|
||||
fun Profile(userId: Long) = FragmentScreen("ProfileFragment") { ProfileFragment(userId) }
|
||||
fun Main() = FragmentScreen { MainFragment() }
|
||||
fun AddressSearch() = FragmentScreen { AddressSearchFragment() }
|
||||
fun Profile(userId: Long) = FragmentScreen("Profile_$userId") { ProfileFragment(userId) }
|
||||
fun Browser(url: String) = ActivityScreen { Intent(Intent.ACTION_VIEW, Uri.parse(url)) }
|
||||
}
|
||||
```
|
||||
|
||||
Additional you can use `FragmentFactory` for creating your screens:
|
||||
```kotlin
|
||||
val SomeScreen = FragmentScreen("SomeScreenId") { factory: FragmentFactory -> ... }
|
||||
fun SomeScreen() = FragmentScreen { factory: FragmentFactory -> ... }
|
||||
```
|
||||
|
||||
## Screen parameters and result listener
|
||||
```kotlin
|
||||
//you have to specify screen parameters via new FragmentScreen creation
|
||||
fun SelectPhoto(resultKey: String) = FragmentScreen {
|
||||
SelectPhotoFragment.getNewInstance(resultKey)
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
//listen result
|
||||
fun onSelectPhotoClicked() {
|
||||
router.setResultListener(RESULT_KEY) { data ->
|
||||
view.showPhoto(data as Bitmap)
|
||||
}
|
||||
router.navigateTo(SelectPhoto(RESULT_KEY))
|
||||
}
|
||||
|
||||
//send result
|
||||
fun onPhotoClick(photo: Bitmap) {
|
||||
router.sendResult(resultKey, photoRes)
|
||||
router.exit()
|
||||
}
|
||||
```
|
||||
|
||||
## Sample
|
||||
To see how to add, initialize and use the library and predefined navigators check out
|
||||
the [GitFox (Android GitLab client)](https://gitlab.com/terrakok/gitlab-client)
|
||||
To see how to add, initialize and use the library and predefined navigators see the **sample project**
|
||||
(thank you [@Javernaut](https://github.com/Javernaut) for support new library version and migrate sample project to Kotlin!)
|
||||
|
||||

|
||||

|
||||

|
||||
For more complex use case check out the [GitFox (Android GitLab client)](https://gitlab.com/terrakok/gitlab-client)
|
||||
|
||||
## Applications that use Cicerone
|
||||
<a href="https://play.google.com/store/apps/details?id=ru.foodfox.client"><img src="https://play-lh.googleusercontent.com/gWYedIqy8QujCQOn0kzEIBEkGLBSpuKvFm-fMcfkWnJ1Oirtv847xAE4OyhAaohdcp5V=s360" width="64" /> Яндекс.Еда — доставка еды/продуктов. Food delivery</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.kms.me"><img src="https://play-lh.googleusercontent.com/IBzu0tlHd_amw2HbjBLOZiCfK-0tn0CnwkMdOd1toP23rdHUV-i7L2ViNKgIg687=s360" width="64" /> Kaspersky Internet Security</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.deliveryclub"><img src="https://play-lh.googleusercontent.com/m6-gFunvj7aQD5fdv8EdJZBN5M4REIobTaPZPYS0K5Td7CNYnazN7fOKiPwwaY3hJw=s360" width="64" /> Delivery Club – Доставка еды и продуктов</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=ru.hh.android"><img src="https://play-lh.googleusercontent.com/YpAV7Q-ZJhI5tzFk_wEX-7-x2BydtnCtFTVUrmq0zAO6jLCLA4nNcfem3p_Pyowg9w=s360" width="64" /> Поиск работы на hh. Вакансии рядом с домом</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.foodient.whisk"><img src="https://play-lh.googleusercontent.com/eKotZjJcZOU2_L9t2l34EEY7aGl5zhvKVuEbF0Kc4MRs_pAC2SJgOnWMkMTFjR_e9EY=s360" width="64" /> Whisk: Recipe Saver, Meal Planner & Grocery List</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=kz.beeline.odp"><img src="https://play-lh.googleusercontent.com/hzgjpQQpy6Z-Byye0aVKSv9P7h8yx58i6pVkQtiM6jB99iWFXjYfKeaPqJ3wm6Rtb38=s360" width="64" /> Мой Beeline (Казахстан)</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.mercuryo.app"><img src="https://play-lh.googleusercontent.com/FKulXdc15r5PWX6hTZi2i3iaJjcQHwd9xParp6YPiQ2KiBqza7jwEt_b_tqLwXpyEHg=s360" width="64" /> Mercuryo Bitcoin Cryptowallet</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.warefly.checkscan"><img src="https://play-lh.googleusercontent.com/2c2uuiSl2vwGgp-vdI-VArQEMdSSXk1neUK5A-Udc0WANPcvp5kBJFEugrFiXnxUc7k=s360" width="64" /> ЧекСкан - кэшбэк за чеки, цены и акции в магазинах</a><br>
|
||||
<a href="https://github.com/eduard1abdulmanov123/News"><img src="https://raw.githubusercontent.com/eduard1abdulmanov123/News/dev/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png" width="64" /> RSS Reader для Вести.Ru</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=com.epam.connect.android"><img src="https://play-lh.googleusercontent.com/aN7R6BiR7yt7b3oEoBI30pVwzsdzaWe3TWpw8c9igqoOj79Pm2xVh4_C4qwjSKwjVio=s360" width="64" /> EPAM Connect</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=org.consumerreports.ratings"><img src="https://play-lh.googleusercontent.com/dEdOwZOjXAdamytxY1TgY8LS-Hc9FKCcit5HP1RyaKqRAWjDJEyFSQS1XlqQPpeY5UI=s360" width="64" /> Consumer Reports: Product Reviews & Ratings</a><br>
|
||||
<a href="https://play.google.com/store/apps/details?id=ru.zakaz.android"><img src="https://play-lh.googleusercontent.com/jj18yK2dB2MHZ_QdO21aXyznGXteIF2q4mgxY4ubLhFv9gwZqHVDeu1i2FmanS-0Furm=s360" width="64" /> Zakaz.ru</a><br>
|
||||
|
||||
## Participants
|
||||
+ idea and code - Konstantin Tskhovrebov (@terrakok)
|
||||
+ architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)
|
||||
+ Idea and code - Konstantin Tskhovrebov (@terrakok)
|
||||
+ Architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)
|
||||
|
||||
## License
|
||||
```
|
||||
|
||||
+4
-7
@@ -1,24 +1,21 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.10'
|
||||
ext.kotlin_version = '1.5.21'
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// For the library uploading to the Bintray
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
group = publishedGroupId // Maven Group ID for the artifact
|
||||
|
||||
install {
|
||||
repositories.mavenInstaller {
|
||||
// This generates POM.xml with proper parameters
|
||||
pom.project {
|
||||
packaging 'jar'
|
||||
groupId publishedGroupId
|
||||
artifactId artifact
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
apply plugin: 'com.jfrog.bintray'
|
||||
|
||||
version = libraryVersion // Library version
|
||||
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
|
||||
bintray {
|
||||
// User and ApiKey stored in local.properties
|
||||
user = properties.getProperty("bintrayUser")
|
||||
key = properties.getProperty("bintrayApiKey")
|
||||
configurations = ['archives']
|
||||
// Package info for Bintray
|
||||
pkg {
|
||||
repo = bintrayRepo
|
||||
name = bintrayName
|
||||
licenses = allLicenses
|
||||
vcsUrl = gitUrl
|
||||
publish = true
|
||||
}
|
||||
}
|
||||
|
||||
// Dependency to call only bintrayUpload task
|
||||
bintrayUpload.dependsOn install
|
||||
+4
-42
@@ -1,5 +1,4 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
// This is important even if Android Studio claims it isn't
|
||||
// used. Android can't interpret Java 8 byte code.
|
||||
@@ -13,49 +12,12 @@ dependencies {
|
||||
}
|
||||
|
||||
ext {
|
||||
// This params is for the library uploading to the Bintray
|
||||
bintrayRepo = 'terramaven'
|
||||
bintrayName = 'cicerone-kotlin'
|
||||
publishedGroupId = 'com.github.terrakok'
|
||||
artifact = 'cicerone'
|
||||
libraryVersion = '6.0'
|
||||
gitUrl = 'https://github.com/terrakok/Cicerone'
|
||||
allLicenses = ['MIT']
|
||||
libraryVersion = '7.1'
|
||||
}
|
||||
|
||||
// Configuration of the library uploading to the Bintray
|
||||
// Note: Call 'bintrayUpload' task (it will execute 'install' task first)
|
||||
// I try to include as little properties as possible in these files
|
||||
project.archivesBaseName = 'cicerone' // to fix that project name different from artifact name
|
||||
apply from: 'androidmaven.gradle'
|
||||
apply from: 'bintray.gradle'
|
||||
project.archivesBaseName = artifact // to fix that project name different from artifact name
|
||||
|
||||
// Tasks for sources and javadocs jars
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.kotlin.srcDirs
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
groupId = publishedGroupId
|
||||
artifactId = artifact
|
||||
version = libraryVersion
|
||||
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
//apply from: 'publish-mavencentral.gradle'
|
||||
//for publication use './gradlew publishReleasePublicationToSonatypeRepository'
|
||||
@@ -0,0 +1,94 @@
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
Properties properties = new Properties()
|
||||
properties.load(project.rootProject.file('local.properties').newDataInputStream())
|
||||
properties.each { name, value -> ext[name] = value }
|
||||
|
||||
group = publishedGroupId
|
||||
version = libraryVersion
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.kotlin.srcDirs
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
groupId = publishedGroupId
|
||||
artifactId = artifact
|
||||
version = libraryVersion
|
||||
|
||||
artifact("$buildDir/libs/${artifact}-${version}.jar")
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
|
||||
pom {
|
||||
name = artifact
|
||||
description = 'Cicerone is a lightweight library that makes the navigation in an Android app easy.'
|
||||
url = 'https://github.com/terrakok/Cicerone'
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name = "MIT"
|
||||
url = "https://opensource.org/licenses/MIT"
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id = 'terrakok'
|
||||
name = 'Konstantin Tskhovrebov'
|
||||
email = 'terrakok@gmail.com'
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
developerConnection = 'scm:git:ssh://github.com/terrakok/Cicerone.git'
|
||||
url = 'https://github.com/terrakok/Cicerone'
|
||||
}
|
||||
|
||||
// A slightly hacky fix so that your POM will include any transitive dependencies
|
||||
// that your library builds upon
|
||||
withXml {
|
||||
def dependenciesNode = asNode().appendNode('dependencies')
|
||||
|
||||
project.configurations.implementation.allDependencies.each {
|
||||
def dependencyNode = dependenciesNode.appendNode('dependency')
|
||||
dependencyNode.appendNode('groupId', it.group)
|
||||
dependencyNode.appendNode('artifactId', it.name)
|
||||
dependencyNode.appendNode('version', it.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "sonatype"
|
||||
url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
credentials {
|
||||
username = ossrhUsername
|
||||
password = ossrhPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
sign publishing.publications
|
||||
}
|
||||
|
||||
tasks.getByName("publishReleasePublicationToSonatypeRepository").dependsOn("assemble", "sourcesJar", "javadocJar")
|
||||
@@ -7,6 +7,27 @@ package com.github.terrakok.cicerone
|
||||
*/
|
||||
abstract class BaseRouter {
|
||||
internal val commandBuffer = CommandBuffer()
|
||||
private val resultWire = ResultWire()
|
||||
|
||||
/**
|
||||
* Sets data listener with given key
|
||||
* and returns [ResultListenerHandler] for availability to dispose subscription.
|
||||
*
|
||||
* After first call listener will be removed.
|
||||
*/
|
||||
fun setResultListener(
|
||||
key: String,
|
||||
listener: ResultListener
|
||||
): ResultListenerHandler {
|
||||
return resultWire.setResultListener(key, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends data to listener with given key.
|
||||
*/
|
||||
fun sendResult(key: String, data: Any) {
|
||||
resultWire.sendResult(key, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends navigation command array to [CommandBuffer].
|
||||
|
||||
@@ -17,12 +17,14 @@ class Cicerone<T : BaseRouter> private constructor(val router: T) {
|
||||
/**
|
||||
* Creates the Cicerone instance with the default [Router]
|
||||
*/
|
||||
@JvmStatic
|
||||
fun create() = create(Router())
|
||||
|
||||
/**
|
||||
* Creates the Cicerone instance with the custom router.
|
||||
* @param customRouter the custom router extending [BaseRouter]
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T : BaseRouter> create(customRouter: T) = Cicerone(customRouter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
|
||||
/**
|
||||
* Passes navigation command to an active [Navigator]
|
||||
* or stores it in the pending commands queue to pass it later.
|
||||
@@ -7,6 +10,7 @@ package com.github.terrakok.cicerone
|
||||
internal class CommandBuffer : NavigatorHolder {
|
||||
private var navigator: Navigator? = null
|
||||
private val pendingCommands = mutableListOf<Array<out Command>>()
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
override fun setNavigator(navigator: Navigator) {
|
||||
this.navigator = navigator
|
||||
@@ -24,6 +28,8 @@ internal class CommandBuffer : NavigatorHolder {
|
||||
* @param commands navigation command array
|
||||
*/
|
||||
fun executeCommands(commands: Array<out Command>) {
|
||||
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
|
||||
mainHandler.post {
|
||||
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Interface definition for a result callback.
|
||||
*/
|
||||
fun interface ResultListener {
|
||||
fun onResult(data: Any)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for manual delete subscription and avoid leak
|
||||
*/
|
||||
fun interface ResultListenerHandler {
|
||||
fun dispose()
|
||||
}
|
||||
|
||||
internal class ResultWire {
|
||||
private val listeners = mutableMapOf<String, ResultListener>()
|
||||
|
||||
fun setResultListener(key: String, listener: ResultListener): ResultListenerHandler {
|
||||
listeners[key] = listener
|
||||
return ResultListenerHandler {
|
||||
listeners.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendResult(key: String, data: Any) {
|
||||
listeners.remove(key)?.onResult(data)
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,9 @@ open class Router : BaseRouter() {
|
||||
* Open new screen and add it to the screens chain.
|
||||
*
|
||||
* @param screen screen
|
||||
* @param clearContainer if FALSE then new screen shows over previous
|
||||
*/
|
||||
fun navigateTo(screen: Screen, clearContainer: Boolean = true) {
|
||||
executeCommands(Forward(screen, clearContainer))
|
||||
fun navigateTo(screen: Screen) {
|
||||
executeCommands(Forward(screen))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,10 +55,9 @@ open class Router : BaseRouter() {
|
||||
* Opens several screens inside single transaction.
|
||||
*
|
||||
* @param screens
|
||||
* @param showOnlyTopScreenView if FALSE then all screen views show together
|
||||
*/
|
||||
fun newChain(vararg screens: Screen, showOnlyTopScreenView: Boolean = true) {
|
||||
val commands = screens.map { Forward(it, showOnlyTopScreenView) }
|
||||
fun newChain(vararg screens: Screen) {
|
||||
val commands = screens.map { Forward(it) }
|
||||
executeCommands(*commands.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -67,10 +65,14 @@ open class Router : BaseRouter() {
|
||||
* Clear current stack and open several screens inside single transaction.
|
||||
*
|
||||
* @param screens
|
||||
* @param showOnlyTopScreenView if FALSE then all screen views show together
|
||||
*/
|
||||
fun newRootChain(vararg screens: Screen, showOnlyTopScreenView: Boolean = true) {
|
||||
val commands = screens.map { Forward(it, showOnlyTopScreenView) }
|
||||
fun newRootChain(vararg screens: Screen) {
|
||||
val commands = screens.mapIndexed { index, screen ->
|
||||
if (index == 0)
|
||||
Replace(screen)
|
||||
else
|
||||
Forward(screen)
|
||||
}
|
||||
executeCommands(BackTo(null), *commands.toTypedArray())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Screen is class for description application screen.
|
||||
* Screen is interface for description application screen.
|
||||
*/
|
||||
abstract class Screen {
|
||||
open val screenKey: String = this::class.qualifiedName!!
|
||||
interface Screen {
|
||||
val screenKey: String get() = this::class.java.name
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.github.terrakok.cicerone.androidx
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import androidx.fragment.app.*
|
||||
import com.github.terrakok.cicerone.*
|
||||
import com.github.terrakok.cicerone.androidx.TransactionInfo.Type.ADD
|
||||
import com.github.terrakok.cicerone.androidx.TransactionInfo.Type.REPLACE
|
||||
|
||||
/**
|
||||
* Navigator implementation for launch fragments and activities.
|
||||
@@ -13,14 +12,14 @@ import com.github.terrakok.cicerone.androidx.TransactionInfo.Type.REPLACE
|
||||
*
|
||||
* Recommendation: most useful for Single-Activity application.
|
||||
*/
|
||||
open class AppNavigator constructor(
|
||||
open class AppNavigator @JvmOverloads constructor(
|
||||
protected val activity: FragmentActivity,
|
||||
protected val containerId: Int,
|
||||
protected val fragmentManager: FragmentManager = activity.supportFragmentManager,
|
||||
protected val fragmentFactory: FragmentFactory = FragmentFactory()
|
||||
protected val fragmentFactory: FragmentFactory = fragmentManager.fragmentFactory
|
||||
) : Navigator {
|
||||
|
||||
protected val localStackCopy = mutableListOf<TransactionInfo>()
|
||||
protected val localStackCopy = mutableListOf<String>()
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
fragmentManager.executePendingTransactions()
|
||||
@@ -40,8 +39,7 @@ open class AppNavigator constructor(
|
||||
private fun copyStackToLocal() {
|
||||
localStackCopy.clear()
|
||||
for (i in 0 until fragmentManager.backStackEntryCount) {
|
||||
val str = fragmentManager.getBackStackEntryAt(i).name
|
||||
localStackCopy.add(TransactionInfo.fromString(str))
|
||||
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,19 +58,18 @@ open class AppNavigator constructor(
|
||||
}
|
||||
|
||||
protected open fun forward(command: Forward) {
|
||||
when (val screen = command.screen as AppScreen) {
|
||||
when (val screen = command.screen) {
|
||||
is ActivityScreen -> {
|
||||
checkAndStartActivity(screen)
|
||||
}
|
||||
is FragmentScreen -> {
|
||||
val type = if (command.clearContainer) REPLACE else ADD
|
||||
commitNewFragmentScreen(screen, type, true)
|
||||
commitNewFragmentScreen(screen, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun replace(command: Replace) {
|
||||
when (val screen = command.screen as AppScreen) {
|
||||
when (val screen = command.screen) {
|
||||
is ActivityScreen -> {
|
||||
checkAndStartActivity(screen)
|
||||
activity.finish()
|
||||
@@ -80,10 +77,10 @@ open class AppNavigator constructor(
|
||||
is FragmentScreen -> {
|
||||
if (localStackCopy.isNotEmpty()) {
|
||||
fragmentManager.popBackStack()
|
||||
val removed = localStackCopy.removeAt(localStackCopy.lastIndex)
|
||||
commitNewFragmentScreen(screen, removed.type, true)
|
||||
localStackCopy.removeAt(localStackCopy.lastIndex)
|
||||
commitNewFragmentScreen(screen, true)
|
||||
} else {
|
||||
commitNewFragmentScreen(screen, REPLACE, false)
|
||||
commitNewFragmentScreen(screen, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,24 +101,25 @@ open class AppNavigator constructor(
|
||||
|
||||
protected open fun commitNewFragmentScreen(
|
||||
screen: FragmentScreen,
|
||||
type: TransactionInfo.Type,
|
||||
addToBackStack: Boolean
|
||||
) {
|
||||
val fragment = screen.createFragment(fragmentFactory)
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
transaction.setReorderingAllowed(true)
|
||||
setupFragmentTransaction(
|
||||
screen,
|
||||
transaction,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment
|
||||
)
|
||||
when (type) {
|
||||
ADD -> transaction.add(containerId, fragment)
|
||||
REPLACE -> transaction.replace(containerId, fragment)
|
||||
if (screen.clearContainer) {
|
||||
transaction.replace(containerId, fragment, screen.screenKey)
|
||||
} else {
|
||||
transaction.add(containerId, fragment, screen.screenKey)
|
||||
}
|
||||
if (addToBackStack) {
|
||||
val transactionInfo = TransactionInfo(screen.screenKey, type)
|
||||
transaction.addToBackStack(transactionInfo.toString())
|
||||
localStackCopy.add(transactionInfo)
|
||||
transaction.addToBackStack(screen.screenKey)
|
||||
localStackCopy.add(screen.screenKey)
|
||||
}
|
||||
transaction.commit()
|
||||
}
|
||||
@@ -134,13 +132,13 @@ open class AppNavigator constructor(
|
||||
backToRoot()
|
||||
} else {
|
||||
val screenKey = command.screen.screenKey
|
||||
val index = localStackCopy.indexOfFirst { it.screenKey == screenKey }
|
||||
val index = localStackCopy.indexOfFirst { it == screenKey }
|
||||
if (index != -1) {
|
||||
val forRemove = localStackCopy.subList(index, localStackCopy.size)
|
||||
fragmentManager.popBackStack(forRemove.first().toString(), 0)
|
||||
forRemove.clear()
|
||||
} else {
|
||||
backToUnexisting(command.screen as AppScreen)
|
||||
backToUnexisting(command.screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,9 +158,10 @@ open class AppNavigator constructor(
|
||||
* @param nextFragment next screen fragment
|
||||
*/
|
||||
protected open fun setupFragmentTransaction(
|
||||
screen: FragmentScreen,
|
||||
fragmentTransaction: FragmentTransaction,
|
||||
currentFragment: Fragment?,
|
||||
nextFragment: Fragment?
|
||||
nextFragment: Fragment
|
||||
) {
|
||||
// Do nothing by default
|
||||
}
|
||||
@@ -170,9 +169,9 @@ open class AppNavigator constructor(
|
||||
private fun checkAndStartActivity(screen: ActivityScreen) {
|
||||
// Check if we can start activity
|
||||
val activityIntent = screen.createIntent(activity)
|
||||
if (activityIntent.resolveActivity(activity.packageManager) != null) {
|
||||
try {
|
||||
activity.startActivity(activityIntent, screen.startActivityOptions)
|
||||
} else {
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
unexistingActivity(screen, activityIntent)
|
||||
}
|
||||
}
|
||||
@@ -196,7 +195,7 @@ open class AppNavigator constructor(
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
protected open fun backToUnexisting(screen: AppScreen) {
|
||||
protected open fun backToUnexisting(screen: Screen) {
|
||||
backToRoot()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,16 +7,40 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentFactory
|
||||
import com.github.terrakok.cicerone.Screen
|
||||
|
||||
sealed class AppScreen : Screen()
|
||||
fun interface Creator<A, R> {
|
||||
fun create(argument: A): R
|
||||
}
|
||||
|
||||
open class FragmentScreen(
|
||||
override val screenKey: String,
|
||||
val createFragment: (FragmentFactory) -> Fragment
|
||||
) : AppScreen()
|
||||
interface FragmentScreen : Screen {
|
||||
val clearContainer: Boolean get() = true
|
||||
fun createFragment(factory: FragmentFactory): Fragment
|
||||
|
||||
open class ActivityScreen(
|
||||
override val screenKey: String,
|
||||
val createIntent: (context: Context) -> Intent
|
||||
) : AppScreen() {
|
||||
open val startActivityOptions: Bundle? = null
|
||||
companion object {
|
||||
operator fun invoke(
|
||||
key: String? = null,
|
||||
clearContainer: Boolean = true,
|
||||
fragmentCreator: Creator<FragmentFactory, Fragment>
|
||||
) = object : FragmentScreen {
|
||||
override val screenKey = key ?: fragmentCreator::class.java.name
|
||||
override val clearContainer = clearContainer
|
||||
override fun createFragment(factory: FragmentFactory) = fragmentCreator.create(factory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ActivityScreen : Screen {
|
||||
val startActivityOptions: Bundle? get() = null
|
||||
fun createIntent(context: Context): Intent
|
||||
|
||||
companion object {
|
||||
operator fun invoke(
|
||||
key: String? = null,
|
||||
startActivityOptions: Bundle? = null,
|
||||
intentCreator: Creator<Context, Intent>
|
||||
) = object : ActivityScreen {
|
||||
override val screenKey = key ?: intentCreator::class.java.name
|
||||
override val startActivityOptions = startActivityOptions
|
||||
override fun createIntent(context: Context) = intentCreator.create(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.github.terrakok.cicerone.androidx
|
||||
|
||||
data class TransactionInfo(
|
||||
val screenKey: String,
|
||||
val type: Type
|
||||
) {
|
||||
enum class Type(val symbol: Char) {
|
||||
ADD('+'),
|
||||
REPLACE('-');
|
||||
}
|
||||
|
||||
override fun toString() = screenKey + type.symbol
|
||||
|
||||
companion object {
|
||||
fun fromString(str: String) = TransactionInfo(
|
||||
str.dropLast(1),
|
||||
if (str.last() == Type.ADD.symbol) Type.ADD else Type.REPLACE
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,7 @@ interface Command
|
||||
/**
|
||||
* Opens new screen.
|
||||
*/
|
||||
data class Forward(
|
||||
val screen: Screen,
|
||||
val clearContainer: Boolean
|
||||
) : Command
|
||||
data class Forward(val screen: Screen) : Command
|
||||
|
||||
/**
|
||||
* Replaces the current screen.
|
||||
|
||||
@@ -36,6 +36,10 @@ public class FragmentManager {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentFactory getFragmentFactory() {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public interface BackStackEntry {
|
||||
int getId();
|
||||
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
package androidx.fragment.app;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class FragmentTransaction {
|
||||
public FragmentTransaction add(int containerViewId, Fragment fragment) {
|
||||
public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public final FragmentTransaction add(
|
||||
int containerViewId,
|
||||
Class<? extends Fragment> fragmentClass,
|
||||
Bundle args) {
|
||||
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public final FragmentTransaction replace(
|
||||
int containerViewId,
|
||||
Class<? extends Fragment> fragmentClass,
|
||||
Bundle args) {
|
||||
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -42,10 +42,10 @@ android {
|
||||
}
|
||||
|
||||
ext {
|
||||
androidXVersion = "1.2.0"
|
||||
materialVersion = "1.2.1"
|
||||
moxyVersion = "2.2.0"
|
||||
daggerVersion = "2.29.1"
|
||||
androidXVersion = "1.3.0"
|
||||
materialVersion = "1.4.0"
|
||||
moxyVersion = "2.2.2"
|
||||
daggerVersion = "2.38"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -69,5 +69,5 @@ dependencies {
|
||||
implementation ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
|
||||
exclude group: "com.android.support", module: "design"
|
||||
}
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.core:core-ktx:1.6.0"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.github.terrakok.cicerone.sample.ui.bottom.ForwardFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.bottom.TabContainerFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.main.MainActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.main.SampleFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.main.SemiTransparentFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.start.StartActivity
|
||||
|
||||
/**
|
||||
@@ -20,43 +21,47 @@ import com.github.terrakok.cicerone.sample.ui.start.StartActivity
|
||||
*/
|
||||
object Screens {
|
||||
|
||||
fun sampleScreen(number: Int) = FragmentScreen("SampleScreen_$number") {
|
||||
fun Sample(number: Int) = FragmentScreen("Sample($number)") {
|
||||
SampleFragment.getNewInstance(number)
|
||||
}
|
||||
|
||||
fun startScreen() = ActivityScreen("StartScreen") {
|
||||
fun Start() = ActivityScreen {
|
||||
Intent(it, StartActivity::class.java)
|
||||
}
|
||||
|
||||
fun mainScreen() = ActivityScreen("MainScreen") {
|
||||
fun Main() = ActivityScreen {
|
||||
Intent(it, MainActivity::class.java)
|
||||
}
|
||||
|
||||
fun bottomNavigationScreen() = ActivityScreen("BottomNavigationScreen") {
|
||||
fun BottomNavigation() = ActivityScreen {
|
||||
Intent(it, BottomNavigationActivity::class.java)
|
||||
}
|
||||
|
||||
fun tabScreen(tabName: String) = FragmentScreen("FragmentScreen") {
|
||||
fun Tab(tabName: String) = FragmentScreen {
|
||||
TabContainerFragment.getNewInstance(tabName)
|
||||
}
|
||||
|
||||
fun forwardScreen(containerName: String, number: Int) = FragmentScreen("ForwardScreen") {
|
||||
fun Forward(containerName: String, number: Int) = FragmentScreen {
|
||||
ForwardFragment.getNewInstance(containerName, number)
|
||||
}
|
||||
|
||||
fun githubScreen() = ActivityScreen("GithubScreen") {
|
||||
fun Github() = ActivityScreen {
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"))
|
||||
}
|
||||
|
||||
fun profileScreen() = ActivityScreen("ProfileScreen") {
|
||||
fun Profile() = ActivityScreen {
|
||||
Intent(it, ProfileActivity::class.java)
|
||||
}
|
||||
|
||||
fun profileInfoScreen() = FragmentScreen("ProfileInfoScreen") {
|
||||
fun ProfileInfo() = FragmentScreen {
|
||||
ProfileFragment()
|
||||
}
|
||||
|
||||
fun selectPhotoScreen() = FragmentScreen("SelectPhotoScreen") {
|
||||
SelectPhotoFragment()
|
||||
fun SelectPhoto(resultKey: String) = FragmentScreen {
|
||||
SelectPhotoFragment.getNewInstance(resultKey)
|
||||
}
|
||||
|
||||
fun SemiTransparent() = FragmentScreen(clearContainer = false) {
|
||||
SemiTransparentFragment()
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.github.terrakok.cicerone.sample.dagger
|
||||
|
||||
import com.github.terrakok.cicerone.sample.dagger.module.LocalNavigationModule
|
||||
import com.github.terrakok.cicerone.sample.dagger.module.NavigationModule
|
||||
import com.github.terrakok.cicerone.sample.dagger.module.PhotoSelectionModule
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment
|
||||
@@ -10,6 +9,7 @@ import com.github.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.bottom.TabContainerFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.main.MainActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.main.SampleFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.main.SemiTransparentFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.start.StartActivity
|
||||
import dagger.Component
|
||||
import javax.inject.Singleton
|
||||
@@ -20,8 +20,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
@Component(modules = [
|
||||
NavigationModule::class,
|
||||
LocalNavigationModule::class,
|
||||
PhotoSelectionModule::class]
|
||||
LocalNavigationModule::class]
|
||||
)
|
||||
interface AppComponent {
|
||||
fun inject(activity: StartActivity)
|
||||
@@ -39,4 +38,6 @@ interface AppComponent {
|
||||
fun inject(fragment: SelectPhotoFragment)
|
||||
|
||||
fun inject(activity: ProfileActivity)
|
||||
|
||||
fun inject(fragment: SemiTransparentFragment)
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.github.terrakok.cicerone.sample.dagger.module
|
||||
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.PhotoSelection
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
@Module
|
||||
object PhotoSelectionModule {
|
||||
|
||||
private val photoSelection = PhotoSelection(R.drawable.ava_1)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePhotoSelection() = photoSelection
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.animation
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 22.09.18.
|
||||
*/
|
||||
class PhotoSelection(private var selectedPhoto: Int) {
|
||||
|
||||
private var listener: Listener? = null
|
||||
|
||||
fun getSelectedPhoto(): Int {
|
||||
return selectedPhoto
|
||||
}
|
||||
|
||||
fun setSelectedPhoto(selectedPhoto: Int) {
|
||||
this.selectedPhoto = selectedPhoto
|
||||
listener?.onChange(selectedPhoto)
|
||||
}
|
||||
|
||||
fun setListener(listener: Listener?) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onChange(selectedPhoto: Int)
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -2,7 +2,6 @@ package com.github.terrakok.cicerone.sample.mvp.animation.photos
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.PhotoSelection
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
@@ -11,7 +10,7 @@ import moxy.MvpPresenter
|
||||
*/
|
||||
@InjectViewState
|
||||
class SelectPhotoPresenter(
|
||||
private val photoSelection: PhotoSelection,
|
||||
private val resultKey: String,
|
||||
private val router: Router
|
||||
) : MvpPresenter<SelectPhotoView>() {
|
||||
|
||||
@@ -26,7 +25,7 @@ class SelectPhotoPresenter(
|
||||
}
|
||||
|
||||
fun onPhotoClick(photoRes: Int) {
|
||||
photoSelection.setSelectedPhoto(photoRes)
|
||||
router.sendResult(resultKey, photoRes)
|
||||
router.exit()
|
||||
}
|
||||
|
||||
|
||||
+19
-22
@@ -1,8 +1,9 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.animation.profile
|
||||
|
||||
import com.github.terrakok.cicerone.ResultListenerHandler
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.Screens.selectPhotoScreen
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.PhotoSelection
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.Screens.SelectPhoto
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
@@ -11,37 +12,33 @@ import moxy.MvpPresenter
|
||||
*/
|
||||
@InjectViewState
|
||||
class ProfilePresenter(
|
||||
private val photoSelection: PhotoSelection,
|
||||
private val router: Router
|
||||
) : MvpPresenter<ProfileView>() {
|
||||
private var resultListenerHandler: ResultListenerHandler? = null
|
||||
|
||||
companion object {
|
||||
private const val RESULT_KEY = "photo_result"
|
||||
private const val DEFAULT_PHOTO = R.drawable.ava_1
|
||||
}
|
||||
|
||||
override fun onFirstViewAttach() {
|
||||
super.onFirstViewAttach()
|
||||
updatePhoto()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
photoSelection.setListener(null)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun updatePhoto() {
|
||||
viewState!!.showPhoto(photoSelection.getSelectedPhoto())
|
||||
viewState!!.showPhoto(DEFAULT_PHOTO)
|
||||
}
|
||||
|
||||
fun onPhotoClicked() {
|
||||
router.navigateTo(selectPhotoScreen())
|
||||
resultListenerHandler = router.setResultListener(RESULT_KEY) { data ->
|
||||
viewState!!.showPhoto(data as Int)
|
||||
}
|
||||
router.navigateTo(SelectPhoto(RESULT_KEY))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
resultListenerHandler?.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
|
||||
init {
|
||||
photoSelection.setListener(object : PhotoSelection.Listener {
|
||||
override fun onChange(selectedPhoto: Int) {
|
||||
updatePhoto()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.bottom.forward
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.Screens.forwardScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.githubScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.Forward
|
||||
import com.github.terrakok.cicerone.sample.Screens.Github
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
@@ -25,11 +25,11 @@ class ForwardPresenter(
|
||||
}
|
||||
|
||||
fun onForwardPressed() {
|
||||
router.navigateTo(forwardScreen(container, number + 1))
|
||||
router.navigateTo(Forward(container, number + 1))
|
||||
}
|
||||
|
||||
fun onGithubPressed() {
|
||||
router.navigateTo(githubScreen())
|
||||
router.navigateTo(Github())
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
|
||||
+22
-9
@@ -3,7 +3,8 @@ package com.github.terrakok.cicerone.sample.mvp.main
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.Screens.sampleScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens
|
||||
import com.github.terrakok.cicerone.sample.Screens.Sample
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
import java.util.concurrent.Executors
|
||||
@@ -29,18 +30,26 @@ class SamplePresenter(
|
||||
}
|
||||
|
||||
fun onForwardCommandClick() {
|
||||
router.navigateTo(sampleScreen(screenNumber + 1))
|
||||
router.navigateTo(Sample(screenNumber + 1))
|
||||
}
|
||||
|
||||
fun onReplaceCommandClick() {
|
||||
router.replaceScreen(sampleScreen(screenNumber + 1))
|
||||
router.replaceScreen(Sample(screenNumber + 1))
|
||||
}
|
||||
|
||||
fun onNewChainCommandClick() {
|
||||
router.newChain(
|
||||
sampleScreen(screenNumber + 1),
|
||||
sampleScreen(screenNumber + 2),
|
||||
sampleScreen(screenNumber + 3)
|
||||
Sample(screenNumber + 1),
|
||||
Sample(screenNumber + 2),
|
||||
Sample(screenNumber + 3)
|
||||
)
|
||||
}
|
||||
|
||||
fun onNewRootChainCommandClick() {
|
||||
router.newRootChain(
|
||||
Sample(screenNumber + 1),
|
||||
Sample(screenNumber + 2),
|
||||
Sample(screenNumber + 3)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,21 +57,25 @@ class SamplePresenter(
|
||||
router.finishChain()
|
||||
}
|
||||
|
||||
fun onForwardNccCommandClick() {
|
||||
router.navigateTo(Screens.SemiTransparent())
|
||||
}
|
||||
|
||||
fun onNewRootCommandClick() {
|
||||
router.newRootScreen(sampleScreen(screenNumber + 1))
|
||||
router.newRootScreen(Sample(screenNumber + 1))
|
||||
}
|
||||
|
||||
fun onForwardWithDelayCommandClick() {
|
||||
future?.cancel(true)
|
||||
future = executorService.schedule({ //WARNING! Navigation must be only in UI thread.
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
router.navigateTo(sampleScreen(screenNumber + 1))
|
||||
router.navigateTo(Sample(screenNumber + 1))
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
fun onBackToCommandClick() {
|
||||
router.backTo(sampleScreen(3))
|
||||
router.backTo(Sample(3))
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.start
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.Screens.bottomNavigationScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.mainScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.profileScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.BottomNavigation
|
||||
import com.github.terrakok.cicerone.sample.Screens.Main
|
||||
import com.github.terrakok.cicerone.sample.Screens.Profile
|
||||
import moxy.MvpPresenter
|
||||
|
||||
/**
|
||||
@@ -12,15 +12,15 @@ import moxy.MvpPresenter
|
||||
class StartActivityPresenter(private val router: Router) : MvpPresenter<StartActivityView>() {
|
||||
|
||||
fun onOrdinaryPressed() {
|
||||
router.navigateTo(mainScreen())
|
||||
router.navigateTo(Main())
|
||||
}
|
||||
|
||||
fun onMultiPressed() {
|
||||
router.navigateTo(bottomNavigationScreen())
|
||||
router.navigateTo(BottomNavigation())
|
||||
}
|
||||
|
||||
fun onResultWithAnimationPressed() {
|
||||
router.navigateTo(profileScreen())
|
||||
router.navigateTo(Profile())
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
|
||||
+14
-9
@@ -10,9 +10,10 @@ import com.github.terrakok.cicerone.Navigator
|
||||
import com.github.terrakok.cicerone.NavigatorHolder
|
||||
import com.github.terrakok.cicerone.Replace
|
||||
import com.github.terrakok.cicerone.androidx.AppNavigator
|
||||
import com.github.terrakok.cicerone.androidx.FragmentScreen
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.Screens.profileInfoScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.ProfileInfo
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
@@ -32,7 +33,7 @@ class ProfileActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(R.layout.activity_container)
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(profileInfoScreen())))
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(ProfileInfo())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,15 +47,19 @@ class ProfileActivity : AppCompatActivity() {
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.container, supportFragmentManager, supportFragmentManager.fragmentFactory) {
|
||||
|
||||
override fun setupFragmentTransaction(fragmentTransaction: FragmentTransaction, currentFragment: Fragment?, nextFragment: Fragment?) {
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.container) {
|
||||
override fun setupFragmentTransaction(
|
||||
screen: FragmentScreen,
|
||||
fragmentTransaction: FragmentTransaction,
|
||||
currentFragment: Fragment?,
|
||||
nextFragment: Fragment
|
||||
) {
|
||||
if (currentFragment is ProfileFragment
|
||||
&& nextFragment is SelectPhotoFragment) {
|
||||
&& nextFragment is SelectPhotoFragment) {
|
||||
setupSharedElementForProfileToSelectPhoto(
|
||||
currentFragment,
|
||||
nextFragment,
|
||||
fragmentTransaction
|
||||
currentFragment,
|
||||
nextFragment,
|
||||
fragmentTransaction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -8,7 +8,6 @@ import android.widget.ImageView
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.databinding.FragmentSelectPhotoBinding
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.PhotoSelection
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoPresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoView
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
|
||||
@@ -29,9 +28,6 @@ class SelectPhotoFragment : MvpAppCompatFragment(), SelectPhotoView, BackButtonL
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
@Inject
|
||||
lateinit var photoSelection: PhotoSelection
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: SelectPhotoPresenter
|
||||
|
||||
@@ -45,8 +41,11 @@ class SelectPhotoFragment : MvpAppCompatFragment(), SelectPhotoView, BackButtonL
|
||||
private val animationDestionationId: Int
|
||||
get() = arguments!!.getInt(ARG_ANIM_DESTINATION)
|
||||
|
||||
private val resultKey: String
|
||||
get() = arguments!!.getString(EXTRA_RESULT_KEY)!!
|
||||
|
||||
@ProvidePresenter
|
||||
fun providePresenter() = SelectPhotoPresenter(photoSelection, router)
|
||||
fun providePresenter() = SelectPhotoPresenter(resultKey, router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
@@ -100,5 +99,14 @@ class SelectPhotoFragment : MvpAppCompatFragment(), SelectPhotoView, BackButtonL
|
||||
|
||||
companion object {
|
||||
private const val ARG_ANIM_DESTINATION = "arg_anim_dest"
|
||||
private const val EXTRA_RESULT_KEY = "extra_result_key"
|
||||
|
||||
fun getNewInstance(resultKey: String): SelectPhotoFragment {
|
||||
return SelectPhotoFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(EXTRA_RESULT_KEY, resultKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-5
@@ -8,7 +8,6 @@ import android.widget.ImageView
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.PhotoSelection
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.profile.ProfilePresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.profile.ProfileView
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
|
||||
@@ -31,11 +30,8 @@ class ProfileFragment : MvpAppCompatFragment(), ProfileView, BackButtonListener
|
||||
@InjectPresenter
|
||||
lateinit var presenter: ProfilePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var photoSelection: PhotoSelection
|
||||
|
||||
@ProvidePresenter
|
||||
fun providePresenter() = ProfilePresenter(photoSelection, router)
|
||||
fun providePresenter() = ProfilePresenter(router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.Screens.tabScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.Tab
|
||||
import com.github.terrakok.cicerone.sample.mvp.bottom.BottomNavigationPresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.bottom.BottomNavigationView
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
@@ -87,7 +87,7 @@ class BottomNavigationActivity : MvpAppCompatActivity(), BottomNavigationView, R
|
||||
if (newFragment == null) {
|
||||
transaction.add(
|
||||
R.id.ab_container,
|
||||
tabScreen(tab).createFragment.invoke(fm.fragmentFactory), tab
|
||||
Tab(tab).createFragment(fm.fragmentFactory), tab
|
||||
)
|
||||
}
|
||||
if (currentFragment != null) {
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.androidx.AppNavigator
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.Screens.forwardScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.Forward
|
||||
import com.github.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
|
||||
@@ -23,7 +23,7 @@ import javax.inject.Inject
|
||||
class TabContainerFragment : Fragment(), RouterProvider, BackButtonListener {
|
||||
|
||||
private val navigator: Navigator by lazy {
|
||||
AppNavigator(activity!!, R.id.ftc_container, childFragmentManager, childFragmentManager.fragmentFactory)
|
||||
AppNavigator(activity!!, R.id.ftc_container, childFragmentManager)
|
||||
}
|
||||
|
||||
@Inject
|
||||
@@ -47,7 +47,7 @@ class TabContainerFragment : Fragment(), RouterProvider, BackButtonListener {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
if (childFragmentManager.findFragmentById(R.id.ftc_container) == null) {
|
||||
cicerone.router.replaceScreen(forwardScreen(containerName, 0))
|
||||
cicerone.router.replaceScreen(Forward(containerName, 0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import androidx.fragment.app.Fragment
|
||||
import moxy.MvpAppCompatFragment
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
abstract class BaseFragment : MvpAppCompatFragment() {
|
||||
abstract class BaseFragment : MvpAppCompatFragment(), ChainScreen {
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
|
||||
@@ -5,4 +5,9 @@ import java.lang.ref.WeakReference
|
||||
|
||||
interface ChainHolder {
|
||||
val chain: MutableList<WeakReference<Fragment>>
|
||||
}
|
||||
|
||||
interface ChainScreen {
|
||||
val name: String
|
||||
val creationTime: Long
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import com.github.terrakok.cicerone.Replace
|
||||
import com.github.terrakok.cicerone.androidx.AppNavigator
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.Screens.sampleScreen
|
||||
import com.github.terrakok.cicerone.sample.Screens.Sample
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import moxy.MvpAppCompatActivity
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -31,7 +31,7 @@ class MainActivity : MvpAppCompatActivity(), ChainHolder {
|
||||
@Inject
|
||||
lateinit var navigatorHolder: NavigatorHolder
|
||||
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.main_container, supportFragmentManager, supportFragmentManager.fragmentFactory) {
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.main_container) {
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
super.applyCommands(commands)
|
||||
@@ -48,7 +48,7 @@ class MainActivity : MvpAppCompatActivity(), ChainHolder {
|
||||
screensSchemeTV = findViewById<View>(R.id.screens_scheme) as TextView
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(sampleScreen(1))))
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(Sample(1))))
|
||||
} else {
|
||||
printScreensScheme()
|
||||
}
|
||||
@@ -75,10 +75,10 @@ class MainActivity : MvpAppCompatActivity(), ChainHolder {
|
||||
}
|
||||
|
||||
private fun printScreensScheme() {
|
||||
val fragments = ArrayList<SampleFragment>()
|
||||
val fragments = ArrayList<ChainScreen>()
|
||||
for (fragmentReference in chain) {
|
||||
val fragment = fragmentReference.get()
|
||||
if (fragment != null && fragment is SampleFragment) {
|
||||
if (fragment != null && fragment is ChainScreen) {
|
||||
fragments.add(fragment)
|
||||
}
|
||||
}
|
||||
@@ -86,9 +86,9 @@ class MainActivity : MvpAppCompatActivity(), ChainHolder {
|
||||
val t = f1.creationTime - f2.creationTime
|
||||
if (t > 0) 1 else if (t < 0) -1 else 0
|
||||
}
|
||||
val keys = ArrayList<Int>()
|
||||
val keys = ArrayList<String>()
|
||||
for (fragment in fragments) {
|
||||
keys.add(fragment.number)
|
||||
keys.add(fragment.name)
|
||||
}
|
||||
screensSchemeTV.text = "Chain: $keys"
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ class SampleFragment : BaseFragment(), SampleView, BackButtonListener {
|
||||
@ProvidePresenter
|
||||
fun createSamplePresenter() = SamplePresenter(router, arguments!!.getInt(EXTRA_NUMBER))
|
||||
|
||||
val number: Int
|
||||
get() = arguments!!.getInt(EXTRA_NUMBER)
|
||||
val creationTime: Long
|
||||
override val name: String
|
||||
get() = arguments!!.getInt(EXTRA_NUMBER).toString()
|
||||
override val creationTime: Long
|
||||
get() = arguments!!.getLong(EXTRA_TIME, 0L)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -54,10 +54,12 @@ class SampleFragment : BaseFragment(), SampleView, BackButtonListener {
|
||||
binding.forwardCommand.setOnClickListener { presenter.onForwardCommandClick() }
|
||||
binding.replaceCommand.setOnClickListener { presenter.onReplaceCommandClick() }
|
||||
binding.newChainCommand.setOnClickListener { presenter.onNewChainCommandClick() }
|
||||
binding.newRootChainCommand.setOnClickListener { presenter.onNewRootChainCommandClick() }
|
||||
binding.newRootCommand.setOnClickListener { presenter.onNewRootCommandClick() }
|
||||
binding.forwardDelayCommand.setOnClickListener { presenter.onForwardWithDelayCommandClick() }
|
||||
binding.backToCommand.setOnClickListener { presenter.onBackToCommandClick() }
|
||||
binding.finishChainCommand.setOnClickListener { presenter.onFinishChainCommandClick() }
|
||||
binding.forwardNccCommand.setOnClickListener { presenter.onForwardNccCommandClick() }
|
||||
}
|
||||
|
||||
override fun setTitle(title: String) {
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.databinding.FragmentSampleBinding
|
||||
import com.github.terrakok.cicerone.sample.databinding.FragmentSemitransparentBinding
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import moxy.MvpAppCompatFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class SemiTransparentFragment : BaseFragment(), BackButtonListener {
|
||||
lateinit var binding: FragmentSemitransparentBinding
|
||||
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
override val name: String = "D"
|
||||
override val creationTime: Long = System.currentTimeMillis()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = FragmentSemitransparentBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
binding.background.setOnClickListener { onBackPressed() }
|
||||
binding.backButton.setOnClickListener { onBackPressed() }
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
router.exit()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class StartActivity : MvpAppCompatActivity(), StartActivityView {
|
||||
@InjectPresenter
|
||||
lateinit var presenter: StartActivityPresenter
|
||||
|
||||
private val navigator: Navigator = AppNavigator(this, -1, supportFragmentManager, supportFragmentManager.fragmentFactory)
|
||||
private val navigator: Navigator = AppNavigator(this, -1)
|
||||
|
||||
@ProvidePresenter
|
||||
fun createStartActivityPresenter() = StartActivityPresenter(router)
|
||||
|
||||
@@ -66,6 +66,18 @@
|
||||
android:text="New chain"
|
||||
android:textSize="28sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/new_root_chain_command"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:background="@color/purple"
|
||||
android:gravity="center"
|
||||
android:text="New root chain"
|
||||
android:textSize="28sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/new_root_command"
|
||||
android:layout_width="0dp"
|
||||
@@ -113,6 +125,17 @@
|
||||
android:gravity="center"
|
||||
android:text="Finish chain"
|
||||
android:textSize="28sp"/>
|
||||
<TextView
|
||||
android:id="@+id/forward_ncc_command"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="4dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_rowWeight="1"
|
||||
android:background="@color/nephritis"
|
||||
android:gravity="center"
|
||||
android:text="Forward not clearing container"
|
||||
android:textSize="28sp"/>
|
||||
|
||||
</GridLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/semitransparent"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
tools:ignore="HardcodedText">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="40dp"
|
||||
android:background="@color/accent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="This is not a dialog.\n This is a regular screen."
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/back_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:minWidth="100dp"
|
||||
android:text="Back"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -4,6 +4,8 @@
|
||||
<color name="primary_dark">#c0392b</color>
|
||||
<color name="accent">#e67e22</color>
|
||||
|
||||
<color name="semitransparent">#99000000</color>
|
||||
|
||||
|
||||
<color name="alizarin">#e74c3c</color>
|
||||
<color name="carrot">#e67e22</color>
|
||||
@@ -11,4 +13,5 @@
|
||||
<color name="greensea">#16a085</color>
|
||||
<color name="nephritis">#27ae60</color>
|
||||
<color name="belizehole">#2980b9</color>
|
||||
<color name="purple">#9329B9</color>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user