Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| df860d2173 | |||
| dbfc6a4f72 | |||
| 69e4632058 | |||
| f38b5685a2 | |||
| b81d6bc8e5 | |||
| ab8036a13e | |||
| a9688db7f5 | |||
| e63f6c2339 | |||
| 1de7a50b58 | |||
| 0efab8731b | |||
| 39d043f5d7 | |||
| f5397994ab | |||
| af272bc587 | |||
| 9e6c08d5c5 | |||
| 9c67451d11 | |||
| 86674c0275 | |||
| 70b8fcbf4f | |||
| 8be84acb80 | |||
| 4872d10d6a | |||
| ac5efa84ed | |||
| 3e982794ec | |||
| a748473697 | |||
| 0734a4eb22 | |||
| 748f2f657c | |||
| 88884ac51e | |||
| 0b35127778 | |||
| 9aeb02cfae | |||
| 7c31cdc1bd | |||
| 05a8ec8f44 | |||
| d08e6b1afe | |||
| cea829ae88 | |||
| dcf1532103 | |||
| 046410e132 | |||
| e80b2d5432 | |||
| 3f1cf714df | |||
| febae878a4 | |||
| 6cc2d3e474 | |||
| dc2f732ec6 | |||
| 5d6880c95f | |||
| e1e2dd8b9b | |||
| 4df0f34e34 | |||
| f36f6a1e44 | |||
| be63c91abe | |||
| 8e9bf90ee6 | |||
| 0ba13efe29 | |||
| d5737bb092 | |||
| 07869ad6e7 | |||
| a57b8ecb3c | |||
| 8e3f41057d | |||
| 7823dd0658 | |||
| 6b195590de |
@@ -1,90 +1,100 @@
|
||||
# Cicerone
|
||||
[](https://bintray.com/terrakok/terramaven/cicerone/_latestVersion)
|
||||
[](https://bintray.com/terrakok/terramaven/cicerone-kotlin/_latestVersion)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
[](https://gitter.im/terrakok/Cicerone)
|
||||
|
||||
[](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.
|
||||
It was designed to be used with the MVP pattern (try [Moxy](https://github.com/Arello-Mobile/Moxy)), but will work great with any architecture.
|
||||
Cicerone (means - a guide, one who conducts 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
|
||||
+ 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
|
||||
+ `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
|
||||
|
||||
**See the sample application**
|
||||
|
||||
## How to add
|
||||
Add the dependency in your build.gradle:
|
||||
```groovy
|
||||
```kotlin
|
||||
dependencies {
|
||||
//Cicerone
|
||||
compile 'ru.terrakok.cicerone:cicerone:X.X.X'
|
||||
implementation("com.github.terrakok:cicerone:X.X.X")
|
||||
}
|
||||
```
|
||||
|
||||
Initialize the library (for example in your Application class):
|
||||
```java
|
||||
public class SampleApplication extends MvpApplication {
|
||||
public static SampleApplication INSTANCE;
|
||||
private Cicerone<Router> cicerone;
|
||||
```kotlin
|
||||
class App : Application() {
|
||||
private val cicerone = Cicerone.create()
|
||||
val router get() = cicerone.router
|
||||
val navigatorHolder get() = cicerone.getNavigatorHolder()
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
INSTANCE = this;
|
||||
|
||||
initCicerone();
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
INSTANCE = this
|
||||
}
|
||||
|
||||
private void initCicerone() {
|
||||
cicerone = Cicerone.create();
|
||||
}
|
||||
|
||||
public NavigatorHolder getNavigatorHolder() {
|
||||
return cicerone.getNavigatorHolder();
|
||||
}
|
||||
|
||||
public Router getRouter() {
|
||||
return cicerone.getRouter();
|
||||
companion object {
|
||||
internal lateinit var INSTANCE: App
|
||||
private set
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How it works?
|
||||
<img src="https://github.com/terrakok/Cicerone/raw/develop/media/CiceroneDiagram.png" alt="drawing" width="800"/>
|
||||
<img src="https://github.com/terrakok/Cicerone/blob/master/media/CiceroneDiagram.png" alt="CiceroneDiagram.png" width="800"/>
|
||||
|
||||
Presenter calls navigation method of Router.
|
||||
|
||||
```java
|
||||
public class SamplePresenter extends Presenter<SampleView> {
|
||||
private Router router;
|
||||
```kotlin
|
||||
class SamplePresenter(
|
||||
private val router: Router
|
||||
) : Presenter<SampleView>() {
|
||||
|
||||
public SamplePresenter() {
|
||||
router = SampleApplication.INSTANCE.getRouter();
|
||||
fun onOpenNewScreen() {
|
||||
router.navigateTo(SomeScreen())
|
||||
}
|
||||
|
||||
public void onBackCommandClick() {
|
||||
router.exit();
|
||||
}
|
||||
|
||||
public void onForwardCommandClick() {
|
||||
router.navigateTo(new SomeScreen());
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -95,14 +105,10 @@ 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.
|
||||
|
||||
```java
|
||||
void executeCommands(Command[] commands) {
|
||||
if (navigator != null) {
|
||||
navigator.applyCommands(commands);
|
||||
} else {
|
||||
pendingCommands.add(commands);
|
||||
}
|
||||
}
|
||||
```kotlin
|
||||
fun executeCommands(commands: Array<out Command>) {
|
||||
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
|
||||
}
|
||||
```
|
||||
|
||||
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
|
||||
@@ -110,51 +116,113 @@ Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in
|
||||
|
||||
**Attention**: Use _onResumeFragments()_ with FragmentActivity ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator);
|
||||
```kotlin
|
||||
private val navigator = AppNavigator(this, R.id.container)
|
||||
|
||||
override fun onResumeFragments() {
|
||||
super.onResumeFragments()
|
||||
navigatorHolder.setNavigator(navigator)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator();
|
||||
override fun onPause() {
|
||||
navigatorHolder.removeNavigator()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private Navigator navigator = new Navigator() {
|
||||
@Override
|
||||
public void applyCommands(Command[] commands) {
|
||||
//implement commands logic (apply command batch to navigation container)
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Navigation commands
|
||||
This commands set 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 navigators
|
||||
The library provides predefined navigators for _Fragments_ and _Activity_.
|
||||
## Predefined navigator
|
||||
The library provides predefined navigator for _Fragments_ and _Activity_.
|
||||
To use, just provide it with the container and _FragmentManager_.
|
||||
```java
|
||||
private Navigator navigator = new SupportAppNavigator(this, R.id.container);
|
||||
```kotlin
|
||||
private val navigator = AppNavigator(this, R.id.container)
|
||||
```
|
||||
## Sample
|
||||
To see how to add, initialize and use the library and predefined navigators check out the sample.
|
||||
Or look at [GitFox (Android GitLab client)](https://gitlab.com/terrakok/gitlab-client)
|
||||
|
||||

|
||||

|
||||

|
||||
Custom navigator can be useful sometimes:
|
||||
```kotlin
|
||||
private val navigator = object : AppNavigator(this, R.id.container) {
|
||||
override fun setupFragmentTransaction(
|
||||
fragmentTransaction: FragmentTransaction,
|
||||
currentFragment: Fragment?,
|
||||
nextFragment: Fragment?
|
||||
) {
|
||||
super.setupFragmentTransaction(fragmentTransaction, currentFragment, nextFragment)
|
||||
fragmentTransaction.setReorderingAllowed(true)
|
||||
}
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
hideKeyboard()
|
||||
super.applyCommands(commands)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Screens
|
||||
Describe your screens as you like e.g. create Kotlin `object` with all application screens:
|
||||
```kotlin
|
||||
object Screens {
|
||||
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
|
||||
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 see **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 with Cicerone inside
|
||||
<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.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)
|
||||
|
||||
+4
-2
@@ -1,15 +1,17 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.4.10'
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.3.0-alpha12'
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
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.7.1'
|
||||
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
|
||||
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Thu Sep 27 15:42:22 MSK 2018
|
||||
#Fri Jan 25 22:04:15 MSK 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
|
||||
|
||||
+23
-6
@@ -1,4 +1,5 @@
|
||||
apply plugin: 'java'
|
||||
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.
|
||||
@@ -8,15 +9,16 @@ targetCompatibility = JavaVersion.VERSION_1_7
|
||||
dependencies {
|
||||
compileOnly project(':stub-android')
|
||||
compileOnly 'com.google.android:android:4.1.1.4'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
}
|
||||
|
||||
ext {
|
||||
// This params is for the library uploading to the Bintray
|
||||
bintrayRepo = 'terramaven'
|
||||
bintrayName = 'cicerone'
|
||||
publishedGroupId = 'ru.terrakok.cicerone'
|
||||
bintrayName = 'cicerone-kotlin'
|
||||
publishedGroupId = 'com.github.terrakok'
|
||||
artifact = 'cicerone'
|
||||
libraryVersion = '4.0.2'
|
||||
libraryVersion = '6.6'
|
||||
gitUrl = 'https://github.com/terrakok/Cicerone'
|
||||
allLicenses = ['MIT']
|
||||
}
|
||||
@@ -24,13 +26,13 @@ ext {
|
||||
// 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
|
||||
project.archivesBaseName = 'cicerone' // to fix that project name different from artifact name
|
||||
apply from: 'androidmaven.gradle'
|
||||
apply from: 'bintray.gradle'
|
||||
|
||||
// Tasks for sources and javadocs jars
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.java.srcDirs
|
||||
from sourceSets.main.kotlin.srcDirs
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
@@ -38,7 +40,22 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
|
||||
/**
|
||||
* BaseRouter is an abstract class to implement high-level navigation.
|
||||
* Extend it to add needed transition methods.
|
||||
*/
|
||||
public abstract class BaseRouter {
|
||||
private CommandBuffer commandBuffer;
|
||||
|
||||
public BaseRouter() {
|
||||
this.commandBuffer = new CommandBuffer();
|
||||
}
|
||||
|
||||
CommandBuffer getCommandBuffer() {
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends navigation command array to {@link CommandBuffer}.
|
||||
*
|
||||
* @param commands navigation command array to execute
|
||||
*/
|
||||
protected void executeCommands(Command... commands) {
|
||||
commandBuffer.executeCommands(commands);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
/**
|
||||
* Cicerone is the holder for other library components.
|
||||
* To use it, instantiate it using one of the {@link #create()} methods.
|
||||
* When you need a {@link NavigatorHolder navigation holder} or router, get it here.
|
||||
*
|
||||
* @param <T> type of router. You can use the default {@link Router} or pass your own
|
||||
* {@link BaseRouter} implementation.
|
||||
*/
|
||||
public class Cicerone<T extends BaseRouter> {
|
||||
private T router;
|
||||
|
||||
private Cicerone(T router) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
public NavigatorHolder getNavigatorHolder() {
|
||||
return router.getCommandBuffer();
|
||||
}
|
||||
|
||||
public T getRouter() {
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Cicerone instance with the default {@link Router router}
|
||||
*/
|
||||
public static Cicerone<Router> create() {
|
||||
return create(new Router());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the Cicerone instance with the custom router.
|
||||
* @param customRouter the custom router extending {@link BaseRouter}
|
||||
*/
|
||||
public static <T extends BaseRouter> Cicerone<T> create(T customRouter) {
|
||||
return new Cicerone<>(customRouter);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
|
||||
/**
|
||||
* Passes navigation command to an active {@link Navigator}
|
||||
* or stores it in the pending commands queue to pass it later.
|
||||
*/
|
||||
class CommandBuffer implements NavigatorHolder {
|
||||
private Navigator navigator;
|
||||
private Queue<Command[]> pendingCommands = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void setNavigator(Navigator navigator) {
|
||||
this.navigator = navigator;
|
||||
while (!pendingCommands.isEmpty()) {
|
||||
if (navigator != null) {
|
||||
executeCommands(pendingCommands.poll());
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNavigator() {
|
||||
this.navigator = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes {@code commands} to the {@link Navigator} if it available.
|
||||
* Else puts it to the pending commands queue to pass it later.
|
||||
* @param commands navigation command array
|
||||
*/
|
||||
void executeCommands(Command[] commands) {
|
||||
if (navigator != null) {
|
||||
navigator.applyCommands(commands);
|
||||
} else {
|
||||
pendingCommands.add(commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
import ru.terrakok.cicerone.commands.Back;
|
||||
import ru.terrakok.cicerone.commands.BackTo;
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
import ru.terrakok.cicerone.commands.Forward;
|
||||
import ru.terrakok.cicerone.commands.Replace;
|
||||
|
||||
/**
|
||||
* Router is the class for high-level navigation.
|
||||
* Use it to perform needed transitions.<br>
|
||||
* This implementation covers almost all cases needed for the average app.
|
||||
* Extend it if you need some tricky navigation.
|
||||
*/
|
||||
public class Router extends BaseRouter {
|
||||
|
||||
public Router() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open new screen and add it to the screens chain.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public void navigateTo(Screen screen) {
|
||||
executeCommands(new Forward(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all screens and open new one as root.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public void newRootScreen(Screen screen) {
|
||||
executeCommands(
|
||||
new BackTo(null),
|
||||
new Replace(screen)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace current screen.
|
||||
* By replacing the screen, you alters the backstack,
|
||||
* so by going fragmentBack you will return to the previous screen
|
||||
* and not to the replaced one.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public void replaceScreen(Screen screen) {
|
||||
executeCommands(new Replace(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return fragmentBack to the needed screen from the chain.
|
||||
* Behavior in the case when no needed screens found depends on
|
||||
* the processing of the {@link BackTo} command in a {@link Navigator} implementation.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public void backTo(Screen screen) {
|
||||
executeCommands(new BackTo(screen));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens several screens inside single transaction.
|
||||
* @param screens
|
||||
*/
|
||||
public void newChain(Screen... screens) {
|
||||
Command[] commands = new Command[screens.length];
|
||||
for (int i = 0; i < commands.length; i++) {
|
||||
commands[i] = new Forward(screens[i]);
|
||||
}
|
||||
executeCommands(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current stack and open several screens inside single transaction.
|
||||
* @param screens
|
||||
*/
|
||||
public void newRootChain(Screen... screens) {
|
||||
Command[] commands = new Command[screens.length + 1];
|
||||
commands[0] = new BackTo(null);
|
||||
if (screens.length > 0) {
|
||||
commands[1] = new Replace(screens[0]);
|
||||
for (int i = 1; i < screens.length; i++) {
|
||||
commands[i + 1] = new Forward(screens[i]);
|
||||
}
|
||||
}
|
||||
executeCommands(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all screens from the chain and exit.
|
||||
* It's mostly used to finish the application or close a supplementary navigation chain.
|
||||
*/
|
||||
public void finishChain() {
|
||||
executeCommands(
|
||||
new BackTo(null),
|
||||
new Back()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to the previous screen in the chain.
|
||||
* Behavior in the case when the current screen is the root depends on
|
||||
* the processing of the {@link Back} command in a {@link Navigator} implementation.
|
||||
*/
|
||||
public void exit() {
|
||||
executeCommands(new Back());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
/**
|
||||
* Screen is class for description application screen.
|
||||
*/
|
||||
public abstract class Screen {
|
||||
protected String screenKey = getClass().getCanonicalName();
|
||||
|
||||
public String getScreenKey() {
|
||||
return screenKey;
|
||||
}
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package ru.terrakok.cicerone.android.pure;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.commands.Back;
|
||||
import ru.terrakok.cicerone.commands.BackTo;
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
import ru.terrakok.cicerone.commands.Forward;
|
||||
import ru.terrakok.cicerone.commands.Replace;
|
||||
|
||||
/**
|
||||
* Navigator implementation for launch fragments and activities.<br>
|
||||
* Feature {@link BackTo} works only for fragments.<br>
|
||||
* Recommendation: most useful for Single-Activity application.
|
||||
*/
|
||||
public class AppNavigator implements Navigator {
|
||||
|
||||
private final Activity activity;
|
||||
private final FragmentManager fragmentManager;
|
||||
private final int containerId;
|
||||
private LinkedList<String> localStackCopy;
|
||||
|
||||
public AppNavigator(Activity activity, int containerId) {
|
||||
this(activity, activity.getFragmentManager(), containerId);
|
||||
}
|
||||
|
||||
public AppNavigator(Activity activity, FragmentManager fragmentManager, int containerId) {
|
||||
this.activity = activity;
|
||||
this.fragmentManager = fragmentManager;
|
||||
this.containerId = containerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCommands(Command[] commands) {
|
||||
fragmentManager.executePendingTransactions();
|
||||
|
||||
//copy stack before apply commands
|
||||
copyStackToLocal();
|
||||
|
||||
for (Command command : commands) {
|
||||
applyCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyStackToLocal() {
|
||||
localStackCopy = new LinkedList<>();
|
||||
|
||||
final int stackSize = fragmentManager.getBackStackEntryCount();
|
||||
for (int i = 0; i < stackSize; i++) {
|
||||
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform transition described by the navigation command
|
||||
*
|
||||
* @param command the navigation command to apply
|
||||
*/
|
||||
protected void applyCommand(Command command) {
|
||||
if (command instanceof Forward) {
|
||||
activityForward((Forward) command);
|
||||
} else if (command instanceof Replace) {
|
||||
activityReplace((Replace) command);
|
||||
} else if (command instanceof BackTo) {
|
||||
backTo((BackTo) command);
|
||||
} else if (command instanceof Back) {
|
||||
fragmentBack();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void activityForward(Forward command) {
|
||||
AppScreen screen = (AppScreen) command.getScreen();
|
||||
Intent activityIntent = screen.getActivityIntent(activity);
|
||||
|
||||
// Start activity
|
||||
if (activityIntent != null) {
|
||||
Bundle options = createStartActivityOptions(command, activityIntent);
|
||||
checkAndStartActivity(screen, activityIntent, options);
|
||||
} else {
|
||||
fragmentForward(command);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fragmentForward(Forward command) {
|
||||
AppScreen screen = (AppScreen) command.getScreen();
|
||||
Fragment fragment = createFragment(screen);
|
||||
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.addToBackStack(screen.getScreenKey())
|
||||
.commit();
|
||||
localStackCopy.add(screen.getScreenKey());
|
||||
}
|
||||
|
||||
protected void fragmentBack() {
|
||||
if (localStackCopy.size() > 0) {
|
||||
fragmentManager.popBackStack();
|
||||
localStackCopy.removeLast();
|
||||
} else {
|
||||
activityBack();
|
||||
}
|
||||
}
|
||||
|
||||
protected void activityBack() {
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
protected void activityReplace(Replace command) {
|
||||
AppScreen screen = (AppScreen) command.getScreen();
|
||||
Intent activityIntent = screen.getActivityIntent(activity);
|
||||
|
||||
// Replace activity
|
||||
if (activityIntent != null) {
|
||||
Bundle options = createStartActivityOptions(command, activityIntent);
|
||||
checkAndStartActivity(screen, activityIntent, options);
|
||||
activity.finish();
|
||||
} else {
|
||||
fragmentReplace(command);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fragmentReplace(Replace command) {
|
||||
AppScreen screen = (AppScreen) command.getScreen();
|
||||
Fragment fragment = createFragment(screen);
|
||||
|
||||
if (localStackCopy.size() > 0) {
|
||||
fragmentManager.popBackStack();
|
||||
localStackCopy.removeLast();
|
||||
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.addToBackStack(screen.getScreenKey())
|
||||
.commit();
|
||||
localStackCopy.add(screen.getScreenKey());
|
||||
|
||||
} else {
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@link BackTo} command transition
|
||||
*/
|
||||
protected void backTo(BackTo command) {
|
||||
if (command.getScreen() == null) {
|
||||
backToRoot();
|
||||
} else {
|
||||
String key = command.getScreen().getScreenKey();
|
||||
int index = localStackCopy.indexOf(key);
|
||||
int size = localStackCopy.size();
|
||||
|
||||
if (index != -1) {
|
||||
for (int i = 1; i < size - index; i++) {
|
||||
localStackCopy.removeLast();
|
||||
}
|
||||
fragmentManager.popBackStack(key, 0);
|
||||
} else {
|
||||
backToUnexisting((AppScreen) command.getScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void backToRoot() {
|
||||
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
localStackCopy.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to setup fragment transaction {@link FragmentTransaction}.
|
||||
* For example: setCustomAnimations(...), addSharedElement(...) or setReorderingAllowed(...)
|
||||
*
|
||||
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
|
||||
* @param currentFragment current fragment in container
|
||||
* (for {@link Replace} command it will be screen previous in new chain, NOT replaced screen)
|
||||
* @param nextFragment next screen fragment
|
||||
* @param fragmentTransaction fragment transaction
|
||||
*/
|
||||
protected void setupFragmentTransaction(Command command,
|
||||
Fragment currentFragment,
|
||||
Fragment nextFragment,
|
||||
FragmentTransaction fragmentTransaction) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to create option for start activity
|
||||
*
|
||||
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
|
||||
* @param activityIntent activity intent
|
||||
* @return transition options
|
||||
*/
|
||||
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkAndStartActivity(AppScreen screen, Intent activityIntent, Bundle options) {
|
||||
// Check if we can start activity
|
||||
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||
activity.startActivity(activityIntent, options);
|
||||
} else {
|
||||
unexistingActivity(screen, activityIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is no activity to open {@code screenKey}.
|
||||
*
|
||||
* @param screen screen
|
||||
* @param activityIntent intent passed to start Activity for the {@code screenKey}
|
||||
*/
|
||||
protected void unexistingActivity(AppScreen screen, Intent activityIntent) {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Fragment matching {@code screenKey}.
|
||||
*
|
||||
* @param screen screen
|
||||
* @return instantiated fragment for the passed screen
|
||||
*/
|
||||
protected Fragment createFragment(AppScreen screen) {
|
||||
Fragment fragment = screen.getFragment();
|
||||
|
||||
if (fragment == null) {
|
||||
errorWhileCreatingScreen(screen);
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we tried to fragmentBack to some specific screen (via {@link BackTo} command),
|
||||
* but didn't found it.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
protected void backToUnexisting(AppScreen screen) {
|
||||
backToRoot();
|
||||
}
|
||||
|
||||
protected void errorWhileCreatingScreen(AppScreen screen) {
|
||||
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package ru.terrakok.cicerone.android.pure;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import ru.terrakok.cicerone.Screen;
|
||||
|
||||
/**
|
||||
* AppScreen is base class for description and creation application screen.<br>
|
||||
* NOTE: If you have described the creation of Intent then Activity will be started.<br>
|
||||
* Recommendation: Use Intents for launch external application.
|
||||
*/
|
||||
public abstract class AppScreen extends Screen {
|
||||
|
||||
public Fragment getFragment() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
package ru.terrakok.cicerone.android.support;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.commands.Back;
|
||||
import ru.terrakok.cicerone.commands.BackTo;
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
import ru.terrakok.cicerone.commands.Forward;
|
||||
import ru.terrakok.cicerone.commands.Replace;
|
||||
|
||||
/**
|
||||
* Navigator implementation for launch fragments and activities.<br>
|
||||
* Feature {@link BackTo} works only for fragments.<br>
|
||||
* Recommendation: most useful for Single-Activity application.
|
||||
*/
|
||||
public class SupportAppNavigator implements Navigator {
|
||||
|
||||
private final Activity activity;
|
||||
private final FragmentManager fragmentManager;
|
||||
private final int containerId;
|
||||
private LinkedList<String> localStackCopy;
|
||||
|
||||
public SupportAppNavigator(FragmentActivity activity, int containerId) {
|
||||
this(activity, activity.getSupportFragmentManager(), containerId);
|
||||
}
|
||||
|
||||
public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) {
|
||||
this.activity = activity;
|
||||
this.fragmentManager = fragmentManager;
|
||||
this.containerId = containerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyCommands(Command[] commands) {
|
||||
fragmentManager.executePendingTransactions();
|
||||
|
||||
//copy stack before apply commands
|
||||
copyStackToLocal();
|
||||
|
||||
for (Command command : commands) {
|
||||
applyCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyStackToLocal() {
|
||||
localStackCopy = new LinkedList<>();
|
||||
|
||||
final int stackSize = fragmentManager.getBackStackEntryCount();
|
||||
for (int i = 0; i < stackSize; i++) {
|
||||
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform transition described by the navigation command
|
||||
*
|
||||
* @param command the navigation command to apply
|
||||
*/
|
||||
protected void applyCommand(Command command) {
|
||||
if (command instanceof Forward) {
|
||||
activityForward((Forward) command);
|
||||
} else if (command instanceof Replace) {
|
||||
activityReplace((Replace) command);
|
||||
} else if (command instanceof BackTo) {
|
||||
backTo((BackTo) command);
|
||||
} else if (command instanceof Back) {
|
||||
fragmentBack();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void activityForward(Forward command) {
|
||||
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
|
||||
Intent activityIntent = screen.getActivityIntent(activity);
|
||||
|
||||
// Start activity
|
||||
if (activityIntent != null) {
|
||||
Bundle options = createStartActivityOptions(command, activityIntent);
|
||||
checkAndStartActivity(screen, activityIntent, options);
|
||||
} else {
|
||||
fragmentForward(command);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fragmentForward(Forward command) {
|
||||
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
|
||||
Fragment fragment = createFragment(screen);
|
||||
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.addToBackStack(screen.getScreenKey())
|
||||
.commit();
|
||||
localStackCopy.add(screen.getScreenKey());
|
||||
}
|
||||
|
||||
protected void fragmentBack() {
|
||||
if (localStackCopy.size() > 0) {
|
||||
fragmentManager.popBackStack();
|
||||
localStackCopy.removeLast();
|
||||
} else {
|
||||
activityBack();
|
||||
}
|
||||
}
|
||||
|
||||
protected void activityBack() {
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
protected void activityReplace(Replace command) {
|
||||
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
|
||||
Intent activityIntent = screen.getActivityIntent(activity);
|
||||
|
||||
// Replace activity
|
||||
if (activityIntent != null) {
|
||||
Bundle options = createStartActivityOptions(command, activityIntent);
|
||||
checkAndStartActivity(screen, activityIntent, options);
|
||||
activity.finish();
|
||||
} else {
|
||||
fragmentReplace(command);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fragmentReplace(Replace command) {
|
||||
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
|
||||
Fragment fragment = createFragment(screen);
|
||||
|
||||
if (localStackCopy.size() > 0) {
|
||||
fragmentManager.popBackStack();
|
||||
localStackCopy.removeLast();
|
||||
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.addToBackStack(screen.getScreenKey())
|
||||
.commit();
|
||||
localStackCopy.add(screen.getScreenKey());
|
||||
|
||||
} else {
|
||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||
|
||||
setupFragmentTransaction(
|
||||
command,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
|
||||
fragmentTransaction
|
||||
.replace(containerId, fragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs {@link BackTo} command transition
|
||||
*/
|
||||
protected void backTo(BackTo command) {
|
||||
if (command.getScreen() == null) {
|
||||
backToRoot();
|
||||
} else {
|
||||
String key = command.getScreen().getScreenKey();
|
||||
int index = localStackCopy.indexOf(key);
|
||||
int size = localStackCopy.size();
|
||||
|
||||
if (index != -1) {
|
||||
for (int i = 1; i < size - index; i++) {
|
||||
localStackCopy.removeLast();
|
||||
}
|
||||
fragmentManager.popBackStack(key, 0);
|
||||
} else {
|
||||
backToUnexisting((SupportAppScreen) command.getScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void backToRoot() {
|
||||
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
localStackCopy.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to setup fragment transaction {@link FragmentTransaction}.
|
||||
* For example: setCustomAnimations(...), addSharedElement(...) or setReorderingAllowed(...)
|
||||
*
|
||||
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
|
||||
* @param currentFragment current fragment in container
|
||||
* (for {@link Replace} command it will be screen previous in new chain, NOT replaced screen)
|
||||
* @param nextFragment next screen fragment
|
||||
* @param fragmentTransaction fragment transaction
|
||||
*/
|
||||
protected void setupFragmentTransaction(Command command,
|
||||
Fragment currentFragment,
|
||||
Fragment nextFragment,
|
||||
FragmentTransaction fragmentTransaction) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to create option for start activity
|
||||
*
|
||||
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
|
||||
* @param activityIntent activity intent
|
||||
* @return transition options
|
||||
*/
|
||||
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkAndStartActivity(SupportAppScreen screen, Intent activityIntent, Bundle options) {
|
||||
// Check if we can start activity
|
||||
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
|
||||
activity.startActivity(activityIntent, options);
|
||||
} else {
|
||||
unexistingActivity(screen, activityIntent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is no activity to open {@code screenKey}.
|
||||
*
|
||||
* @param screen screen
|
||||
* @param activityIntent intent passed to start Activity for the {@code screenKey}
|
||||
*/
|
||||
protected void unexistingActivity(SupportAppScreen screen, Intent activityIntent) {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Fragment matching {@code screenKey}.
|
||||
*
|
||||
* @param screen screen
|
||||
* @return instantiated fragment for the passed screen
|
||||
*/
|
||||
protected Fragment createFragment(SupportAppScreen screen) {
|
||||
Fragment fragment = screen.getFragment();
|
||||
|
||||
if (fragment == null) {
|
||||
errorWhileCreatingScreen(screen);
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we tried to fragmentBack to some specific screen (via {@link BackTo} command),
|
||||
* but didn't found it.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
protected void backToUnexisting(SupportAppScreen screen) {
|
||||
backToRoot();
|
||||
}
|
||||
|
||||
protected void errorWhileCreatingScreen(SupportAppScreen screen) {
|
||||
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package ru.terrakok.cicerone.android.support;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import ru.terrakok.cicerone.Screen;
|
||||
|
||||
/**
|
||||
* AppScreen is base class for description and creation application screen.<br>
|
||||
* NOTE: If you have described the creation of Intent then Activity will be started.<br>
|
||||
* Recommendation: Use Intents for launch external application.
|
||||
*/
|
||||
public abstract class SupportAppScreen extends Screen {
|
||||
|
||||
public Fragment getFragment() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone.commands;
|
||||
|
||||
/**
|
||||
* Rolls fragmentBack the last transition from the screens chain.
|
||||
*/
|
||||
public class Back implements Command {
|
||||
|
||||
/**
|
||||
* Creates a {@link Back} navigation command.
|
||||
*/
|
||||
public Back() {
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone.commands;
|
||||
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.Screen;
|
||||
|
||||
/**
|
||||
* Rolls fragmentBack to the needed screen from the screens chain.
|
||||
* Behavior in the case when no needed screens found depends on an implementation of the {@link Navigator}.
|
||||
* But the recommended behavior is to return to the root.
|
||||
*/
|
||||
public class BackTo implements Command {
|
||||
private Screen screen;
|
||||
|
||||
/**
|
||||
* Creates a {@link BackTo} navigation command.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public BackTo(Screen screen) {
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public Screen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone.commands;
|
||||
|
||||
/**
|
||||
* Navigation command describes screens transition.
|
||||
* that can be processed by {@link ru.terrakok.cicerone.Navigator}.
|
||||
*/
|
||||
public interface Command {
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone.commands;
|
||||
|
||||
import ru.terrakok.cicerone.Screen;
|
||||
|
||||
/**
|
||||
* Opens new screen.
|
||||
*/
|
||||
public class Forward implements Command {
|
||||
private Screen screen;
|
||||
|
||||
/**
|
||||
* Creates a {@link Forward} navigation command.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public Forward(Screen screen) {
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public Screen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone.commands;
|
||||
|
||||
import ru.terrakok.cicerone.Screen;
|
||||
|
||||
/**
|
||||
* Replaces the current screen.
|
||||
*/
|
||||
public class Replace implements Command {
|
||||
private Screen screen;
|
||||
|
||||
/**
|
||||
* Creates a {@link Replace} navigation command.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
public Replace(Screen screen) {
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public Screen getScreen() {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* BaseRouter is an abstract class to implement high-level navigation.
|
||||
*
|
||||
* Extend it to add needed transition methods.
|
||||
*/
|
||||
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].
|
||||
*
|
||||
* @param commands navigation command array to execute
|
||||
*/
|
||||
protected fun executeCommands(vararg commands: Command) {
|
||||
commandBuffer.executeCommands(commands)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Cicerone is the holder for other library components.
|
||||
*
|
||||
* To use it, instantiate it using one of the {@link #create()} methods.
|
||||
*
|
||||
* When you need a [NavigatorHolder] or router, get it here.
|
||||
*
|
||||
* @param router type of router. You can use the default [Router] or pass your own [BaseRouter] implementation.
|
||||
*/
|
||||
class Cicerone<T : BaseRouter> private constructor(val router: T) {
|
||||
|
||||
fun getNavigatorHolder(): NavigatorHolder = router.commandBuffer
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Passes navigation command to an active [Navigator]
|
||||
* or stores it in the pending commands queue to pass it later.
|
||||
*/
|
||||
internal class CommandBuffer : NavigatorHolder {
|
||||
private var navigator: Navigator? = null
|
||||
private val pendingCommands = mutableListOf<Array<out Command>>()
|
||||
|
||||
override fun setNavigator(navigator: Navigator) {
|
||||
this.navigator = navigator
|
||||
pendingCommands.forEach { navigator.applyCommands(it) }
|
||||
pendingCommands.clear()
|
||||
}
|
||||
|
||||
override fun removeNavigator() {
|
||||
navigator = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes `commands` to the [Navigator] if it available.
|
||||
* Else puts it to the pending commands queue to pass it later.
|
||||
* @param commands navigation command array
|
||||
*/
|
||||
fun executeCommands(commands: Array<out Command>) {
|
||||
navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -1,21 +1,14 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* The low-level navigation interface.
|
||||
* Navigator is the one who actually performs any transition.
|
||||
*/
|
||||
public interface Navigator {
|
||||
|
||||
interface Navigator {
|
||||
/**
|
||||
* Performs transition described by the navigation command
|
||||
*
|
||||
* @param commands the navigation command array to apply per single transaction
|
||||
*/
|
||||
void applyCommands(Command[] commands);
|
||||
}
|
||||
fun applyCommands(commands: Array<out Command>)
|
||||
}
|
||||
+6
-10
@@ -1,24 +1,20 @@
|
||||
/*
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
*/
|
||||
|
||||
package ru.terrakok.cicerone;
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Navigator holder interface.
|
||||
* Use it to connect a {@link Navigator} to the {@link Cicerone}.
|
||||
*
|
||||
* Use it to connect a [Navigator] to the [Cicerone].
|
||||
*/
|
||||
public interface NavigatorHolder {
|
||||
|
||||
interface NavigatorHolder {
|
||||
/**
|
||||
* Set an active Navigator for the Cicerone and start receive commands.
|
||||
*
|
||||
* @param navigator new active Navigator
|
||||
*/
|
||||
void setNavigator(Navigator navigator);
|
||||
fun setNavigator(navigator: Navigator)
|
||||
|
||||
/**
|
||||
* Remove the current Navigator and stop receive commands.
|
||||
*/
|
||||
void removeNavigator();
|
||||
fun removeNavigator()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Router is the class for high-level navigation.
|
||||
*
|
||||
* Use it to perform needed transitions.
|
||||
* This implementation covers almost all cases needed for the average app.
|
||||
* Extend it if you need some tricky navigation.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun navigateTo(screen: Screen, clearContainer: Boolean = true) {
|
||||
executeCommands(Forward(screen, clearContainer))
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all screens and open new one as root.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
fun newRootScreen(screen: Screen) {
|
||||
executeCommands(BackTo(null), Replace(screen))
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace current screen.
|
||||
*
|
||||
* By replacing the screen, you alters the backstack,
|
||||
* so by going fragmentBack you will return to the previous screen
|
||||
* and not to the replaced one.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
fun replaceScreen(screen: Screen) {
|
||||
executeCommands(Replace(screen))
|
||||
}
|
||||
|
||||
/**
|
||||
* Return fragmentBack to the needed screen from the chain.
|
||||
*
|
||||
* Behavior in the case when no needed screens found depends on
|
||||
* the processing of the [BackTo] command in a [Navigator] implementation.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
fun backTo(screen: Screen?) {
|
||||
executeCommands(BackTo(screen))
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens several screens inside single transaction.
|
||||
*
|
||||
* @param screens
|
||||
* @param showOnlyTopScreenView if FALSE then all screen views show together
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun newChain(vararg screens: Screen, showOnlyTopScreenView: Boolean = true) {
|
||||
val commands = screens.map { Forward(it, showOnlyTopScreenView) }
|
||||
executeCommands(*commands.toTypedArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current stack and open several screens inside single transaction.
|
||||
*
|
||||
* @param screens
|
||||
* @param showOnlyTopScreenView if FALSE then all screen views show together
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun newRootChain(vararg screens: Screen, showOnlyTopScreenView: Boolean = true) {
|
||||
val commands = screens.mapIndexed { index, screen ->
|
||||
if (index == 0)
|
||||
Replace(screen)
|
||||
else
|
||||
Forward(screen, showOnlyTopScreenView)
|
||||
}
|
||||
executeCommands(BackTo(null), *commands.toTypedArray())
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all screens from the chain and exit.
|
||||
*
|
||||
* It's mostly used to finish the application or close a supplementary navigation chain.
|
||||
*/
|
||||
fun finishChain() {
|
||||
executeCommands(BackTo(null), Back())
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to the previous screen in the chain.
|
||||
*
|
||||
* Behavior in the case when the current screen is the root depends on
|
||||
* the processing of the [Back] command in a [Navigator] implementation.
|
||||
*/
|
||||
fun exit() {
|
||||
executeCommands(Back())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Screen is interface for description application screen.
|
||||
*/
|
||||
interface Screen {
|
||||
val screenKey: String get() = this::class.java.name
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
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.
|
||||
*
|
||||
* Feature [BackTo] works only for fragments.
|
||||
*
|
||||
* Recommendation: most useful for Single-Activity application.
|
||||
*/
|
||||
open class AppNavigator @JvmOverloads constructor(
|
||||
protected val activity: FragmentActivity,
|
||||
protected val containerId: Int,
|
||||
protected val fragmentManager: FragmentManager = activity.supportFragmentManager,
|
||||
protected val fragmentFactory: FragmentFactory = fragmentManager.fragmentFactory
|
||||
) : Navigator {
|
||||
|
||||
protected val localStackCopy = mutableListOf<TransactionInfo>()
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
fragmentManager.executePendingTransactions()
|
||||
|
||||
//copy stack before apply commands
|
||||
copyStackToLocal()
|
||||
|
||||
for (command in commands) {
|
||||
try {
|
||||
applyCommand(command)
|
||||
} catch (e: RuntimeException) {
|
||||
errorOnApplyCommand(command, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyStackToLocal() {
|
||||
localStackCopy.clear()
|
||||
for (i in 0 until fragmentManager.backStackEntryCount) {
|
||||
val str = fragmentManager.getBackStackEntryAt(i).name
|
||||
localStackCopy.add(TransactionInfo.fromString(str))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform transition described by the navigation command
|
||||
*
|
||||
* @param command the navigation command to apply
|
||||
*/
|
||||
protected open fun applyCommand(command: Command) {
|
||||
when (command) {
|
||||
is Forward -> forward(command)
|
||||
is Replace -> replace(command)
|
||||
is BackTo -> backTo(command)
|
||||
is Back -> back()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun forward(command: Forward) {
|
||||
when (val screen = command.screen as AppScreen) {
|
||||
is ActivityScreen -> {
|
||||
checkAndStartActivity(screen)
|
||||
}
|
||||
is FragmentScreen -> {
|
||||
val type = if (command.clearContainer) REPLACE else ADD
|
||||
commitNewFragmentScreen(screen, type, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun replace(command: Replace) {
|
||||
when (val screen = command.screen as AppScreen) {
|
||||
is ActivityScreen -> {
|
||||
checkAndStartActivity(screen)
|
||||
activity.finish()
|
||||
}
|
||||
is FragmentScreen -> {
|
||||
if (localStackCopy.isNotEmpty()) {
|
||||
fragmentManager.popBackStack()
|
||||
val removed = localStackCopy.removeAt(localStackCopy.lastIndex)
|
||||
commitNewFragmentScreen(screen, removed.type, true)
|
||||
} else {
|
||||
commitNewFragmentScreen(screen, REPLACE, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun back() {
|
||||
if (localStackCopy.isNotEmpty()) {
|
||||
fragmentManager.popBackStack()
|
||||
localStackCopy.removeAt(localStackCopy.lastIndex)
|
||||
} else {
|
||||
activityBack()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun activityBack() {
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
protected open fun commitNewFragmentScreen(
|
||||
screen: FragmentScreen,
|
||||
type: TransactionInfo.Type,
|
||||
addToBackStack: Boolean
|
||||
) {
|
||||
val fragment = screen.createFragment(fragmentFactory)
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
transaction.setReorderingAllowed(true)
|
||||
setupFragmentTransaction(
|
||||
transaction,
|
||||
fragmentManager.findFragmentById(containerId),
|
||||
fragment
|
||||
)
|
||||
when (type) {
|
||||
ADD -> transaction.add(containerId, fragment, screen.screenKey)
|
||||
REPLACE -> transaction.replace(containerId, fragment, screen.screenKey)
|
||||
}
|
||||
if (addToBackStack) {
|
||||
val transactionInfo = TransactionInfo(screen.screenKey, type)
|
||||
transaction.addToBackStack(transactionInfo.toString())
|
||||
localStackCopy.add(transactionInfo)
|
||||
}
|
||||
transaction.commit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs [BackTo] command transition
|
||||
*/
|
||||
protected open fun backTo(command: BackTo) {
|
||||
if (command.screen == null) {
|
||||
backToRoot()
|
||||
} else {
|
||||
val screenKey = command.screen.screenKey
|
||||
val index = localStackCopy.indexOfFirst { it.screenKey == 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun backToRoot() {
|
||||
localStackCopy.clear()
|
||||
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to setup fragment transaction [FragmentTransaction].
|
||||
* For example: setCustomAnimations(...), addSharedElement(...) or setReorderingAllowed(...)
|
||||
*
|
||||
* @param fragmentTransaction fragment transaction
|
||||
* @param currentFragment current fragment in container
|
||||
* (for [Replace] command it will be screen previous in new chain, NOT replaced screen)
|
||||
* @param nextFragment next screen fragment
|
||||
*/
|
||||
protected open fun setupFragmentTransaction(
|
||||
fragmentTransaction: FragmentTransaction,
|
||||
currentFragment: Fragment?,
|
||||
nextFragment: Fragment?
|
||||
) {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
private fun checkAndStartActivity(screen: ActivityScreen) {
|
||||
// Check if we can start activity
|
||||
val activityIntent = screen.createIntent(activity)
|
||||
try {
|
||||
activity.startActivity(activityIntent, screen.startActivityOptions)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
unexistingActivity(screen, activityIntent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is no activity to open `screenKey`.
|
||||
*
|
||||
* @param screen screen
|
||||
* @param activityIntent intent passed to start Activity for the `screenKey`
|
||||
*/
|
||||
protected open fun unexistingActivity(
|
||||
screen: ActivityScreen,
|
||||
activityIntent: Intent
|
||||
) {
|
||||
// Do nothing by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we tried to fragmentBack to some specific screen (via [BackTo] command),
|
||||
* but didn't found it.
|
||||
*
|
||||
* @param screen screen
|
||||
*/
|
||||
protected open fun backToUnexisting(screen: AppScreen) {
|
||||
backToRoot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you want to handle apply command error.
|
||||
*
|
||||
* @param command command
|
||||
* @param error error
|
||||
*/
|
||||
protected open fun errorOnApplyCommand(
|
||||
command: Command,
|
||||
error: RuntimeException
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.github.terrakok.cicerone.androidx
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
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 @JvmOverloads constructor(
|
||||
private val key: String? = null,
|
||||
private val fragmentCreator: Creator<FragmentFactory, Fragment>
|
||||
) : AppScreen() {
|
||||
override val screenKey: String get() = key ?: super.screenKey
|
||||
fun createFragment(factory: FragmentFactory) = fragmentCreator.create(factory)
|
||||
}
|
||||
|
||||
open class ActivityScreen @JvmOverloads constructor(
|
||||
private val key: String? = null,
|
||||
private val intentCreator: Creator<Context, Intent>
|
||||
) : AppScreen() {
|
||||
override val screenKey: String get() = key ?: super.screenKey
|
||||
open val startActivityOptions: Bundle? = null
|
||||
fun createIntent(context: Context) = intentCreator.create(context)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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 {
|
||||
@JvmStatic
|
||||
fun fromString(str: String) = TransactionInfo(
|
||||
str.dropLast(1),
|
||||
if (str.last() == Type.ADD.symbol) Type.ADD else Type.REPLACE
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.github.terrakok.cicerone
|
||||
|
||||
/**
|
||||
* Navigation command describes screens transition.
|
||||
*
|
||||
* That can be processed by [com.github.terrakok.cicerone.Navigator]
|
||||
*/
|
||||
interface Command
|
||||
|
||||
/**
|
||||
* Opens new screen.
|
||||
*/
|
||||
data class Forward(
|
||||
val screen: Screen,
|
||||
val clearContainer: Boolean
|
||||
) : Command
|
||||
|
||||
/**
|
||||
* Replaces the current screen.
|
||||
*/
|
||||
data class Replace(val screen: Screen) : Command
|
||||
|
||||
/**
|
||||
* Rolls fragmentBack the last transition from the screens chain.
|
||||
*/
|
||||
class Back : Command
|
||||
|
||||
/**
|
||||
* Rolls fragmentBack to the needed screen from the screens chain.
|
||||
*
|
||||
* Behavior in the case when no needed screens found depends on an implementation of the [com.github.terrakok.cicerone.Navigator]
|
||||
* But the recommended behavior is to return to the root.
|
||||
*/
|
||||
data class BackTo(val screen: Screen?) : Command
|
||||
@@ -1,20 +0,0 @@
|
||||
package android.support.v4.app;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class FragmentTransaction {
|
||||
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentTransaction addToBackStack(String name) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public int commit() {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
package android.support.v4.app;
|
||||
package androidx.fragment.app;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
package android.support.v4.app;
|
||||
package androidx.fragment.app;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package androidx.fragment.app;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class FragmentFactory {
|
||||
}
|
||||
+6
-2
@@ -1,7 +1,7 @@
|
||||
package android.support.v4.app;
|
||||
package androidx.fragment.app;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
@@ -36,6 +36,10 @@ public class FragmentManager {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentFactory getFragmentFactory() {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public interface BackStackEntry {
|
||||
int getId();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package androidx.fragment.app;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class FragmentTransaction {
|
||||
public FragmentTransaction setReorderingAllowed(boolean reorderingAllowed) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public FragmentTransaction addToBackStack(String name) {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
|
||||
public int commit() {
|
||||
throw new RuntimeException("Stub!");
|
||||
}
|
||||
}
|
||||
+29
-12
@@ -1,18 +1,20 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
applicationId "ru.terrakok.cicerone.sample"
|
||||
applicationId "com.github.terrakok.cicerone.sample"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -24,33 +26,48 @@ android {
|
||||
debug {
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
supportLibraryVersion = "25.3.0"
|
||||
moxyVersion = "1.4.6"
|
||||
daggerVersion = "2.10"
|
||||
androidXVersion = "1.2.0"
|
||||
materialVersion = "1.2.1"
|
||||
moxyVersion = "2.2.0"
|
||||
daggerVersion = "2.29.1"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Support libraries
|
||||
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
|
||||
implementation "com.android.support:design:$supportLibraryVersion"
|
||||
implementation "androidx.appcompat:appcompat:$androidXVersion"
|
||||
implementation "com.google.android.material:material:$materialVersion"
|
||||
|
||||
//MVP Moxy
|
||||
implementation "com.arello-mobile:moxy:$moxyVersion"
|
||||
implementation "com.arello-mobile:moxy-app-compat:$moxyVersion"
|
||||
annotationProcessor "com.arello-mobile:moxy-compiler:$moxyVersion"
|
||||
implementation "com.github.moxy-community:moxy:$moxyVersion"
|
||||
implementation "com.github.moxy-community:moxy-androidx:$moxyVersion"
|
||||
kapt "com.github.moxy-community:moxy-compiler:$moxyVersion"
|
||||
|
||||
//Cicerone
|
||||
implementation project(':library')
|
||||
|
||||
//DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
//Bottom Navigation Bar
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="ru.terrakok.cicerone.sample"
|
||||
package="com.github.terrakok.cicerone.sample"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.terrakok.cicerone.sample
|
||||
|
||||
import android.app.Application
|
||||
import com.github.terrakok.cicerone.sample.dagger.AppComponent
|
||||
import com.github.terrakok.cicerone.sample.dagger.DaggerAppComponent
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
class SampleApplication : Application() {
|
||||
|
||||
val appComponent: AppComponent by lazy {
|
||||
DaggerAppComponent.builder().build()
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
INSTANCE = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var INSTANCE: SampleApplication
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.github.terrakok.cicerone.sample
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import com.github.terrakok.cicerone.androidx.ActivityScreen
|
||||
import com.github.terrakok.cicerone.androidx.FragmentScreen
|
||||
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
|
||||
import com.github.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity
|
||||
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.start.StartActivity
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
object Screens {
|
||||
|
||||
fun Sample(number: Int) = FragmentScreen("Sample($number)") {
|
||||
SampleFragment.getNewInstance(number)
|
||||
}
|
||||
|
||||
fun Start() = ActivityScreen {
|
||||
Intent(it, StartActivity::class.java)
|
||||
}
|
||||
|
||||
fun Main() = ActivityScreen {
|
||||
Intent(it, MainActivity::class.java)
|
||||
}
|
||||
|
||||
fun BottomNavigation() = ActivityScreen {
|
||||
Intent(it, BottomNavigationActivity::class.java)
|
||||
}
|
||||
|
||||
fun Tab(tabName: String) = FragmentScreen {
|
||||
TabContainerFragment.getNewInstance(tabName)
|
||||
}
|
||||
|
||||
fun Forward(containerName: String, number: Int) = FragmentScreen {
|
||||
ForwardFragment.getNewInstance(containerName, number)
|
||||
}
|
||||
|
||||
fun Github() = ActivityScreen {
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"))
|
||||
}
|
||||
|
||||
fun Profile() = ActivityScreen {
|
||||
Intent(it, ProfileActivity::class.java)
|
||||
}
|
||||
|
||||
fun ProfileInfo() = FragmentScreen {
|
||||
ProfileFragment()
|
||||
}
|
||||
|
||||
fun SelectPhoto(resultKey: String) = FragmentScreen {
|
||||
SelectPhotoFragment.getNewInstance(resultKey)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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.ui.animations.ProfileActivity
|
||||
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.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.start.StartActivity
|
||||
import dagger.Component
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
@Singleton
|
||||
@Component(modules = [
|
||||
NavigationModule::class,
|
||||
LocalNavigationModule::class]
|
||||
)
|
||||
interface AppComponent {
|
||||
fun inject(activity: StartActivity)
|
||||
|
||||
fun inject(activity: MainActivity)
|
||||
|
||||
fun inject(fragment: SampleFragment)
|
||||
|
||||
fun inject(activity: BottomNavigationActivity)
|
||||
|
||||
fun inject(fragment: TabContainerFragment)
|
||||
|
||||
fun inject(fragment: ProfileFragment)
|
||||
|
||||
fun inject(fragment: SelectPhotoFragment)
|
||||
|
||||
fun inject(activity: ProfileActivity)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.github.terrakok.cicerone.sample.dagger.module
|
||||
|
||||
import com.github.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
@Module
|
||||
object LocalNavigationModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLocalNavigationHolder(): LocalCiceroneHolder = LocalCiceroneHolder()
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.github.terrakok.cicerone.sample.dagger.module
|
||||
|
||||
import com.github.terrakok.cicerone.Cicerone
|
||||
import com.github.terrakok.cicerone.Cicerone.Companion.create
|
||||
import com.github.terrakok.cicerone.NavigatorHolder
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
@Module
|
||||
class NavigationModule {
|
||||
private val cicerone: Cicerone<Router> = create()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRouter(): Router {
|
||||
return cicerone.router
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNavigatorHolder(): NavigatorHolder {
|
||||
return cicerone.getNavigatorHolder()
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.animation.photos
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.R
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
@InjectViewState
|
||||
class SelectPhotoPresenter(
|
||||
private val resultKey: String,
|
||||
private val router: Router
|
||||
) : MvpPresenter<SelectPhotoView>() {
|
||||
|
||||
override fun onFirstViewAttach() {
|
||||
super.onFirstViewAttach()
|
||||
viewState!!.showPhotos(intArrayOf(
|
||||
R.drawable.ava_1,
|
||||
R.drawable.ava_2,
|
||||
R.drawable.ava_3,
|
||||
R.drawable.ava_4
|
||||
))
|
||||
}
|
||||
|
||||
fun onPhotoClick(photoRes: Int) {
|
||||
router.sendResult(resultKey, photoRes)
|
||||
router.exit()
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.animation.photos
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
interface SelectPhotoView : MvpView {
|
||||
fun showPhotos(resourceIds: IntArray)
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
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.R
|
||||
import com.github.terrakok.cicerone.sample.Screens.SelectPhoto
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
@InjectViewState
|
||||
class ProfilePresenter(
|
||||
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()
|
||||
viewState!!.showPhoto(DEFAULT_PHOTO)
|
||||
}
|
||||
|
||||
fun onPhotoClicked() {
|
||||
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()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.animation.profile
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
interface ProfileView : MvpView {
|
||||
fun showPhoto(resId: Int)
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.bottom
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
@InjectViewState
|
||||
class BottomNavigationPresenter(
|
||||
private val router: Router
|
||||
) : MvpPresenter<BottomNavigationView>() {
|
||||
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.bottom
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
interface BottomNavigationView : MvpView
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.bottom.forward
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.Screens.Forward
|
||||
import com.github.terrakok.cicerone.sample.Screens.Github
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
@InjectViewState
|
||||
class ForwardPresenter(
|
||||
private val container: String,
|
||||
private val router: Router,
|
||||
private val number: Int
|
||||
) : MvpPresenter<ForwardView>() {
|
||||
|
||||
private fun createChain(number: Int): String {
|
||||
var chain = "[0]"
|
||||
for (i in 0 until number) {
|
||||
chain += "➔" + (i + 1)
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
fun onForwardPressed() {
|
||||
router.navigateTo(Forward(container, number + 1))
|
||||
}
|
||||
|
||||
fun onGithubPressed() {
|
||||
router.navigateTo(Github())
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
|
||||
init {
|
||||
viewState?.setChainText(createChain(number))
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.bottom.forward
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
interface ForwardView : MvpView {
|
||||
fun setChainText(chainText: String)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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.Sample
|
||||
import moxy.InjectViewState
|
||||
import moxy.MvpPresenter
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
@InjectViewState
|
||||
class SamplePresenter(
|
||||
private val router: Router,
|
||||
private val screenNumber: Int
|
||||
) : MvpPresenter<SampleView>() {
|
||||
|
||||
private val executorService = Executors.newSingleThreadScheduledExecutor()
|
||||
|
||||
private var future: ScheduledFuture<*>? = null
|
||||
|
||||
fun onBackCommandClick() {
|
||||
router.exit()
|
||||
}
|
||||
|
||||
fun onForwardCommandClick() {
|
||||
router.navigateTo(Sample(screenNumber + 1))
|
||||
}
|
||||
|
||||
fun onReplaceCommandClick() {
|
||||
router.replaceScreen(Sample(screenNumber + 1))
|
||||
}
|
||||
|
||||
fun onNewChainCommandClick() {
|
||||
router.newChain(
|
||||
Sample(screenNumber + 1),
|
||||
Sample(screenNumber + 2),
|
||||
Sample(screenNumber + 3)
|
||||
)
|
||||
}
|
||||
|
||||
fun onNewRootChainCommandClick() {
|
||||
router.newRootChain(
|
||||
Sample(screenNumber + 1),
|
||||
Sample(screenNumber + 2),
|
||||
Sample(screenNumber + 3)
|
||||
)
|
||||
}
|
||||
|
||||
fun onFinishChainCommandClick() {
|
||||
router.finishChain()
|
||||
}
|
||||
|
||||
fun onNewRootCommandClick() {
|
||||
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(Sample(screenNumber + 1))
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
fun onBackToCommandClick() {
|
||||
router.backTo(Sample(3))
|
||||
}
|
||||
|
||||
init {
|
||||
viewState?.setTitle("Screen $screenNumber")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.main
|
||||
|
||||
import moxy.MvpView
|
||||
import moxy.viewstate.strategy.AddToEndSingleStrategy
|
||||
import moxy.viewstate.strategy.StateStrategyType
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy::class)
|
||||
interface SampleView : MvpView {
|
||||
fun setTitle(title: String)
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.start
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
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
|
||||
|
||||
/**
|
||||
* Created by terrakok 21.11.16
|
||||
*/
|
||||
class StartActivityPresenter(private val router: Router) : MvpPresenter<StartActivityView>() {
|
||||
|
||||
fun onOrdinaryPressed() {
|
||||
router.navigateTo(Main())
|
||||
}
|
||||
|
||||
fun onMultiPressed() {
|
||||
router.navigateTo(BottomNavigation())
|
||||
}
|
||||
|
||||
fun onResultWithAnimationPressed() {
|
||||
router.navigateTo(Profile())
|
||||
}
|
||||
|
||||
fun onBackPressed() {
|
||||
router.exit()
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.github.terrakok.cicerone.sample.mvp.start
|
||||
|
||||
import moxy.MvpView
|
||||
|
||||
/**
|
||||
* Created by terrakok 21.11.16
|
||||
*/
|
||||
interface StartActivityView : MvpView
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.github.terrakok.cicerone.sample.subnavigation
|
||||
|
||||
import com.github.terrakok.cicerone.Cicerone
|
||||
import com.github.terrakok.cicerone.Cicerone.Companion.create
|
||||
import com.github.terrakok.cicerone.Router
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by terrakok 27.11.16
|
||||
*/
|
||||
class LocalCiceroneHolder {
|
||||
private val containers = HashMap<String, Cicerone<Router>>()
|
||||
|
||||
fun getCicerone(containerTag: String): Cicerone<Router> =
|
||||
containers.getOrPut(containerTag) {
|
||||
create()
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.animations
|
||||
|
||||
import android.os.Bundle
|
||||
import android.transition.ChangeBounds
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.github.terrakok.cicerone.Command
|
||||
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.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
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
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
class ProfileActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var navigatorHolder: NavigatorHolder
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_container)
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(ProfileInfo())))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResumeFragments() {
|
||||
super.onResumeFragments()
|
||||
navigatorHolder.setNavigator(navigator)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
navigatorHolder.removeNavigator()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.container) {
|
||||
|
||||
override fun setupFragmentTransaction(fragmentTransaction: FragmentTransaction, currentFragment: Fragment?, nextFragment: Fragment?) {
|
||||
if (currentFragment is ProfileFragment
|
||||
&& nextFragment is SelectPhotoFragment) {
|
||||
setupSharedElementForProfileToSelectPhoto(
|
||||
currentFragment,
|
||||
nextFragment,
|
||||
fragmentTransaction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSharedElementForProfileToSelectPhoto(profileFragment: ProfileFragment,
|
||||
selectPhotoFragment: SelectPhotoFragment,
|
||||
fragmentTransaction: FragmentTransaction) {
|
||||
val changeBounds = ChangeBounds()
|
||||
selectPhotoFragment.sharedElementEnterTransition = changeBounds
|
||||
selectPhotoFragment.sharedElementReturnTransition = changeBounds
|
||||
profileFragment.sharedElementEnterTransition = changeBounds
|
||||
profileFragment.sharedElementReturnTransition = changeBounds
|
||||
val view = profileFragment.avatarViewForAnimation
|
||||
fragmentTransaction.addSharedElement(view!!, PHOTO_TRANSITION)
|
||||
selectPhotoFragment.setAnimationDestinationId((view.tag as Int))
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val fragment = supportFragmentManager.findFragmentById(R.id.container)
|
||||
if (fragment != null && fragment is BackButtonListener
|
||||
&& (fragment as BackButtonListener).onBackPressed()) {
|
||||
return
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PHOTO_TRANSITION = "photo_trasition"
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.animations.photos
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.photos.SelectPhotoPresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoView
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import moxy.MvpAppCompatFragment
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
class SelectPhotoFragment : MvpAppCompatFragment(), SelectPhotoView, BackButtonListener {
|
||||
|
||||
private lateinit var binding: FragmentSelectPhotoBinding
|
||||
private lateinit var allImages: Array<ImageView>
|
||||
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: SelectPhotoPresenter
|
||||
|
||||
fun setAnimationDestinationId(resId: Int) {
|
||||
var arguments = arguments
|
||||
if (arguments == null) arguments = Bundle()
|
||||
arguments.putInt(ARG_ANIM_DESTINATION, resId)
|
||||
setArguments(arguments)
|
||||
}
|
||||
|
||||
private val animationDestionationId: Int
|
||||
get() = arguments!!.getInt(ARG_ANIM_DESTINATION)
|
||||
|
||||
private val resultKey: String
|
||||
get() = arguments!!.getString(EXTRA_RESULT_KEY)!!
|
||||
|
||||
@ProvidePresenter
|
||||
fun providePresenter() = SelectPhotoPresenter(resultKey, router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = FragmentSelectPhotoBinding.inflate(inflater, container, false)
|
||||
allImages = arrayOf(
|
||||
binding.selectImage1,
|
||||
binding.selectImage2,
|
||||
binding.selectImage3,
|
||||
binding.selectImage4
|
||||
)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
allImages.forEach {
|
||||
it.setOnClickListener(clickListener)
|
||||
}
|
||||
}
|
||||
|
||||
private val clickListener = View.OnClickListener { v ->
|
||||
allImages.forEach {
|
||||
it.transitionName = null
|
||||
}
|
||||
v.transitionName = ProfileActivity.PHOTO_TRANSITION
|
||||
presenter.onPhotoClick((v.tag as Int))
|
||||
}
|
||||
|
||||
override fun showPhotos(resourceIds: IntArray) {
|
||||
if (resourceIds.size >= 4) {
|
||||
//for shared element animation
|
||||
val animRes = animationDestionationId
|
||||
|
||||
allImages.forEachIndexed {
|
||||
index, imageView ->
|
||||
imageView.setImageResource(resourceIds[index])
|
||||
imageView.tag = resourceIds[index]
|
||||
imageView.transitionName = if (animRes == resourceIds[index]) ProfileActivity.PHOTO_TRANSITION else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
presenter.onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.animations.profile
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.profile.ProfilePresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.animation.profile.ProfileView
|
||||
import com.github.terrakok.cicerone.sample.ui.animations.ProfileActivity
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import moxy.MvpAppCompatFragment
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
class ProfileFragment : MvpAppCompatFragment(), ProfileView, BackButtonListener {
|
||||
|
||||
private lateinit var avatar: ImageView
|
||||
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: ProfilePresenter
|
||||
|
||||
@ProvidePresenter
|
||||
fun providePresenter() = ProfilePresenter(router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_profile, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
avatar = view.findViewById<View>(R.id.avatar_imageView) as ImageView
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
avatar.transitionName = ProfileActivity.PHOTO_TRANSITION
|
||||
avatar.setOnClickListener { presenter.onPhotoClicked() }
|
||||
}
|
||||
|
||||
override fun showPhoto(resId: Int) {
|
||||
avatar.setImageResource(resId)
|
||||
|
||||
//for shared element animation
|
||||
avatar.tag = resId
|
||||
}
|
||||
|
||||
val avatarViewForAnimation: View?
|
||||
get() = avatar
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
presenter.onBackPressed()
|
||||
return true
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.bottom
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||
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.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
|
||||
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
|
||||
import moxy.MvpAppCompatActivity
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
class BottomNavigationActivity : MvpAppCompatActivity(), BottomNavigationView, RouterProvider {
|
||||
|
||||
private lateinit var bottomNavigationBar: BottomNavigationBar
|
||||
|
||||
@Inject
|
||||
override lateinit var router: Router
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: BottomNavigationPresenter
|
||||
|
||||
@ProvidePresenter
|
||||
fun createBottomNavigationPresenter() = BottomNavigationPresenter(router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_bottom)
|
||||
bottomNavigationBar = findViewById<View>(R.id.ab_bottom_navigation_bar) as BottomNavigationBar
|
||||
|
||||
initViews()
|
||||
if (savedInstanceState == null) {
|
||||
bottomNavigationBar.selectTab(0, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
bottomNavigationBar
|
||||
.addItem(BottomNavigationItem(R.drawable.ic_android_white_24dp, R.string.tab_android))
|
||||
.addItem(BottomNavigationItem(R.drawable.ic_bug_report_white_24dp, R.string.tab_bug))
|
||||
.addItem(BottomNavigationItem(R.drawable.ic_pets_white_24dp, R.string.tab_dog))
|
||||
.initialise()
|
||||
bottomNavigationBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener {
|
||||
override fun onTabSelected(position: Int) {
|
||||
when (position) {
|
||||
0 -> selectTab("ANDROID")
|
||||
1 -> selectTab("BUG")
|
||||
2 -> selectTab("DOG")
|
||||
}
|
||||
bottomNavigationBar.selectTab(position, false)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(position: Int) {}
|
||||
|
||||
override fun onTabReselected(position: Int) {
|
||||
onTabSelected(position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun selectTab(tab: String) {
|
||||
val fm = supportFragmentManager
|
||||
var currentFragment: Fragment? = null
|
||||
val fragments = fm.fragments
|
||||
for (f in fragments) {
|
||||
if (f.isVisible) {
|
||||
currentFragment = f
|
||||
break
|
||||
}
|
||||
}
|
||||
val newFragment = fm.findFragmentByTag(tab)
|
||||
if (currentFragment != null && newFragment != null && currentFragment === newFragment) return
|
||||
val transaction = fm.beginTransaction()
|
||||
if (newFragment == null) {
|
||||
transaction.add(
|
||||
R.id.ab_container,
|
||||
Tab(tab).createFragment(fm.fragmentFactory), tab
|
||||
)
|
||||
}
|
||||
if (currentFragment != null) {
|
||||
transaction.hide(currentFragment)
|
||||
}
|
||||
if (newFragment != null) {
|
||||
transaction.show(newFragment)
|
||||
}
|
||||
transaction.commitNow()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val fm = supportFragmentManager
|
||||
var fragment: Fragment? = null
|
||||
val fragments = fm.fragments
|
||||
for (f in fragments) {
|
||||
if (f.isVisible) {
|
||||
fragment = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if (fragment != null && fragment is BackButtonListener
|
||||
&& (fragment as BackButtonListener).onBackPressed()) {
|
||||
return
|
||||
} else {
|
||||
presenter.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.bottom
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.github.terrakok.cicerone.sample.databinding.FragmentForwardBinding
|
||||
import com.github.terrakok.cicerone.sample.mvp.bottom.forward.ForwardPresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.bottom.forward.ForwardView
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import com.github.terrakok.cicerone.sample.ui.common.RouterProvider
|
||||
import moxy.MvpAppCompatFragment
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
class ForwardFragment : MvpAppCompatFragment(), ForwardView, BackButtonListener {
|
||||
private lateinit var binding: FragmentForwardBinding
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: ForwardPresenter
|
||||
|
||||
@ProvidePresenter
|
||||
fun provideForwardPresenter(): ForwardPresenter {
|
||||
return ForwardPresenter(
|
||||
arguments!!.getString(EXTRA_NAME),
|
||||
(parentFragment as RouterProvider).router,
|
||||
arguments!!.getInt(EXTRA_NUMBER)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = FragmentForwardBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
binding.toolbar.title = arguments!!.getString(EXTRA_NAME)
|
||||
binding.toolbar.setNavigationOnClickListener { presenter.onBackPressed() }
|
||||
binding.forwardButton.setOnClickListener { presenter.onForwardPressed() }
|
||||
binding.githubButton.setOnClickListener { presenter.onGithubPressed() }
|
||||
}
|
||||
|
||||
override fun setChainText(chainText: String) {
|
||||
binding.chainText.text = chainText
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
presenter.onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_NAME = "extra_name"
|
||||
private const val EXTRA_NUMBER = "extra_number"
|
||||
|
||||
fun getNewInstance(name: String?, number: Int): ForwardFragment {
|
||||
return ForwardFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(EXTRA_NAME, name)
|
||||
putInt(EXTRA_NUMBER, number)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.bottom
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.github.terrakok.cicerone.Cicerone
|
||||
import com.github.terrakok.cicerone.Navigator
|
||||
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.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
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
class TabContainerFragment : Fragment(), RouterProvider, BackButtonListener {
|
||||
|
||||
private val navigator: Navigator by lazy {
|
||||
AppNavigator(activity!!, R.id.ftc_container, childFragmentManager)
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var ciceroneHolder: LocalCiceroneHolder
|
||||
|
||||
private val containerName: String
|
||||
get() = arguments!!.getString(EXTRA_NAME)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
private val cicerone: Cicerone<Router>
|
||||
get() = ciceroneHolder.getCicerone(containerName)
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_tab_container, container, false)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
if (childFragmentManager.findFragmentById(R.id.ftc_container) == null) {
|
||||
cicerone.router.replaceScreen(Forward(containerName, 0))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
cicerone.getNavigatorHolder().setNavigator(navigator)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
cicerone.getNavigatorHolder().removeNavigator()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override val router: Router
|
||||
get() = cicerone.router
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
val fragment = childFragmentManager.findFragmentById(R.id.ftc_container)
|
||||
return if (fragment != null && fragment is BackButtonListener
|
||||
&& (fragment as BackButtonListener).onBackPressed()) {
|
||||
true
|
||||
} else {
|
||||
(activity as RouterProvider?)!!.router.exit()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_NAME = "tcf_extra_name"
|
||||
|
||||
fun getNewInstance(name: String?) =
|
||||
TabContainerFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(EXTRA_NAME, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.common
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
interface BackButtonListener {
|
||||
fun onBackPressed(): Boolean
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.common
|
||||
|
||||
import com.github.terrakok.cicerone.Router
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
interface RouterProvider {
|
||||
val router: Router
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.main
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import moxy.MvpAppCompatFragment
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
abstract class BaseFragment : MvpAppCompatFragment() {
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
val activity = activity
|
||||
if (activity is ChainHolder) {
|
||||
(activity as ChainHolder).chain.add(WeakReference<Fragment>(this))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
val activity = activity
|
||||
if (activity is ChainHolder) {
|
||||
val chain = (activity as ChainHolder).chain
|
||||
val it = chain.iterator()
|
||||
while (it.hasNext()) {
|
||||
val fragmentReference = it.next()
|
||||
val fragment = fragmentReference.get()
|
||||
if (fragment != null && fragment === this) {
|
||||
it.remove()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onDetach()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.main
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
interface ChainHolder {
|
||||
val chain: MutableList<WeakReference<Fragment>>
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.github.terrakok.cicerone.Command
|
||||
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.sample.R
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
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
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
class MainActivity : MvpAppCompatActivity(), ChainHolder {
|
||||
|
||||
private lateinit var screensSchemeTV: TextView
|
||||
|
||||
override val chain = ArrayList<WeakReference<Fragment>>()
|
||||
|
||||
@Inject
|
||||
lateinit var navigatorHolder: NavigatorHolder
|
||||
|
||||
private val navigator: Navigator = object : AppNavigator(this, R.id.main_container) {
|
||||
|
||||
override fun applyCommands(commands: Array<out Command>) {
|
||||
super.applyCommands(commands)
|
||||
supportFragmentManager.executePendingTransactions()
|
||||
printScreensScheme()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
screensSchemeTV = findViewById<View>(R.id.screens_scheme) as TextView
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(arrayOf<Command>(Replace(Sample(1))))
|
||||
} else {
|
||||
printScreensScheme()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResumeFragments() {
|
||||
super.onResumeFragments()
|
||||
navigatorHolder.setNavigator(navigator)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
navigatorHolder.removeNavigator()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val fragment = supportFragmentManager.findFragmentById(R.id.main_container)
|
||||
if (fragment != null && fragment is BackButtonListener
|
||||
&& (fragment as BackButtonListener).onBackPressed()) {
|
||||
return
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun printScreensScheme() {
|
||||
val fragments = ArrayList<SampleFragment>()
|
||||
for (fragmentReference in chain) {
|
||||
val fragment = fragmentReference.get()
|
||||
if (fragment != null && fragment is SampleFragment) {
|
||||
fragments.add(fragment)
|
||||
}
|
||||
}
|
||||
fragments.sortWith { f1, f2 ->
|
||||
val t = f1.creationTime - f2.creationTime
|
||||
if (t > 0) 1 else if (t < 0) -1 else 0
|
||||
}
|
||||
val keys = ArrayList<Int>()
|
||||
for (fragment in fragments) {
|
||||
keys.add(fragment.number)
|
||||
}
|
||||
screensSchemeTV.text = "Chain: $keys"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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 com.github.terrakok.cicerone.Router
|
||||
import com.github.terrakok.cicerone.sample.SampleApplication
|
||||
import com.github.terrakok.cicerone.sample.databinding.FragmentSampleBinding
|
||||
import com.github.terrakok.cicerone.sample.mvp.main.SamplePresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.main.SampleView
|
||||
import com.github.terrakok.cicerone.sample.ui.common.BackButtonListener
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
class SampleFragment : BaseFragment(), SampleView, BackButtonListener {
|
||||
|
||||
lateinit var binding: FragmentSampleBinding
|
||||
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: SamplePresenter
|
||||
|
||||
@ProvidePresenter
|
||||
fun createSamplePresenter() = SamplePresenter(router, arguments!!.getInt(EXTRA_NUMBER))
|
||||
|
||||
val number: Int
|
||||
get() = arguments!!.getInt(EXTRA_NUMBER)
|
||||
val creationTime: Long
|
||||
get() = arguments!!.getLong(EXTRA_TIME, 0L)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = FragmentSampleBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener { presenter.onBackCommandClick() }
|
||||
binding.backCommand.setOnClickListener { presenter.onBackCommandClick() }
|
||||
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() }
|
||||
}
|
||||
|
||||
override fun setTitle(title: String) {
|
||||
binding.toolbar.title = title
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
presenter.onBackCommandClick()
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_NUMBER = "extra_number"
|
||||
private const val EXTRA_TIME = "extra_time"
|
||||
|
||||
fun getNewInstance(number: Int): SampleFragment {
|
||||
return SampleFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(EXTRA_NUMBER, number)
|
||||
putLong(EXTRA_TIME, System.currentTimeMillis())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.github.terrakok.cicerone.sample.ui.start
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.github.terrakok.cicerone.Navigator
|
||||
import com.github.terrakok.cicerone.NavigatorHolder
|
||||
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.mvp.start.StartActivityPresenter
|
||||
import com.github.terrakok.cicerone.sample.mvp.start.StartActivityView
|
||||
import moxy.MvpAppCompatActivity
|
||||
import moxy.presenter.InjectPresenter
|
||||
import moxy.presenter.ProvidePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by terrakok 21.11.16
|
||||
*/
|
||||
class StartActivity : MvpAppCompatActivity(), StartActivityView {
|
||||
|
||||
@Inject
|
||||
lateinit var router: Router
|
||||
|
||||
@Inject
|
||||
lateinit var navigatorHolder: NavigatorHolder
|
||||
|
||||
@InjectPresenter
|
||||
lateinit var presenter: StartActivityPresenter
|
||||
|
||||
private val navigator: Navigator = AppNavigator(this, -1)
|
||||
|
||||
@ProvidePresenter
|
||||
fun createStartActivityPresenter() = StartActivityPresenter(router)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
SampleApplication.INSTANCE.appComponent.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_start)
|
||||
initViews()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
findViewById<View>(R.id.ordinary_nav_button).setOnClickListener {
|
||||
presenter.onOrdinaryPressed()
|
||||
}
|
||||
findViewById<View>(R.id.multi_nav_button).setOnClickListener {
|
||||
presenter.onMultiPressed()
|
||||
}
|
||||
findViewById<View>(R.id.result_and_anim_button).setOnClickListener {
|
||||
presenter.onResultWithAnimationPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
navigatorHolder.setNavigator(navigator)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
navigatorHolder.removeNavigator()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
presenter.onBackPressed()
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import ru.terrakok.cicerone.sample.dagger.AppComponent;
|
||||
import ru.terrakok.cicerone.sample.dagger.DaggerAppComponent;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class SampleApplication extends Application {
|
||||
public static SampleApplication INSTANCE;
|
||||
private AppComponent appComponent;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
INSTANCE = this;
|
||||
}
|
||||
|
||||
public AppComponent getAppComponent() {
|
||||
if (appComponent == null) {
|
||||
appComponent = DaggerAppComponent.builder().build();
|
||||
}
|
||||
return appComponent;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import ru.terrakok.cicerone.android.support.SupportAppScreen;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.bottom.ForwardFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.bottom.TabContainerFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.main.SampleFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.start.StartActivity;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class Screens {
|
||||
public static final class SampleScreen extends SupportAppScreen {
|
||||
private final int number;
|
||||
|
||||
public SampleScreen(int number) {
|
||||
this.number = number;
|
||||
this.screenKey = getClass().getSimpleName() + "_" + number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getFragment() {
|
||||
return SampleFragment.getNewInstance(number);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class StartScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return new Intent(context, StartActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MainScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return new Intent(context, MainActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class BottomNavigationScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return new Intent(context, BottomNavigationActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class TabScreen extends SupportAppScreen {
|
||||
private final String tabName;
|
||||
|
||||
public TabScreen(String tabName) {
|
||||
this.tabName = tabName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getFragment() {
|
||||
return TabContainerFragment.getNewInstance(tabName);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ForwardScreen extends SupportAppScreen {
|
||||
private final String containerName;
|
||||
private final int number;
|
||||
|
||||
public ForwardScreen(String containerName, int number) {
|
||||
this.containerName = containerName;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getFragment() {
|
||||
return ForwardFragment.getNewInstance(containerName, number);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class GithubScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ProfileScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Intent getActivityIntent(Context context) {
|
||||
return new Intent(context, ProfileActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ProfileInfoScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Fragment getFragment() {
|
||||
return new ProfileFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public static final class SelectPhotoScreen extends SupportAppScreen {
|
||||
@Override
|
||||
public Fragment getFragment() {
|
||||
return new SelectPhotoFragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.dagger;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
import ru.terrakok.cicerone.sample.dagger.module.LocalNavigationModule;
|
||||
import ru.terrakok.cicerone.sample.dagger.module.NavigationModule;
|
||||
import ru.terrakok.cicerone.sample.dagger.module.PhotoSelectionModule;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.bottom.TabContainerFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.main.SampleFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.start.StartActivity;
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
NavigationModule.class,
|
||||
LocalNavigationModule.class,
|
||||
PhotoSelectionModule.class
|
||||
})
|
||||
public interface AppComponent {
|
||||
|
||||
void inject(StartActivity activity);
|
||||
|
||||
void inject(MainActivity activity);
|
||||
|
||||
void inject(SampleFragment fragment);
|
||||
|
||||
void inject(BottomNavigationActivity activity);
|
||||
|
||||
void inject(TabContainerFragment fragment);
|
||||
|
||||
void inject(ProfileFragment fragment);
|
||||
|
||||
void inject(SelectPhotoFragment fragment);
|
||||
|
||||
void inject(ProfileActivity activity);
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.dagger.module;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import ru.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder;
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
|
||||
@Module
|
||||
public class LocalNavigationModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
LocalCiceroneHolder provideLocalNavigationHolder() {
|
||||
return new LocalCiceroneHolder();
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.dagger.module;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import ru.terrakok.cicerone.Cicerone;
|
||||
import ru.terrakok.cicerone.NavigatorHolder;
|
||||
import ru.terrakok.cicerone.Router;
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
|
||||
@Module
|
||||
public class NavigationModule {
|
||||
private Cicerone<Router> cicerone;
|
||||
|
||||
public NavigationModule() {
|
||||
cicerone = Cicerone.create();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Router provideRouter() {
|
||||
return cicerone.getRouter();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
NavigatorHolder provideNavigatorHolder() {
|
||||
return cicerone.getNavigatorHolder();
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.dagger.module;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
|
||||
|
||||
/**
|
||||
* Created by terrakok 24.11.16
|
||||
*/
|
||||
|
||||
@Module
|
||||
public class PhotoSelectionModule {
|
||||
private PhotoSelection photoSelection = new PhotoSelection(R.drawable.ava_1);
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
PhotoSelection providePhotoSelectionr() {
|
||||
return photoSelection;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.animation;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 22.09.18.
|
||||
*/
|
||||
public class PhotoSelection {
|
||||
private int selectedPhoto;
|
||||
private Listener listener;
|
||||
|
||||
public PhotoSelection(int defaultPhoto) {
|
||||
this.selectedPhoto = defaultPhoto;
|
||||
}
|
||||
|
||||
public int getSelectedPhoto() {
|
||||
return selectedPhoto;
|
||||
}
|
||||
|
||||
public void setSelectedPhoto(int selectedPhoto) {
|
||||
this.selectedPhoto = selectedPhoto;
|
||||
if (listener != null) {
|
||||
listener.onChange(selectedPhoto);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener(Listener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onChange(int selectedPhoto);
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.animation.photos;
|
||||
|
||||
import com.arellomobile.mvp.InjectViewState;
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
@InjectViewState
|
||||
public class SelectPhotoPresenter extends MvpPresenter<SelectPhotoView> {
|
||||
private Router router;
|
||||
private PhotoSelection photoSelection;
|
||||
|
||||
public SelectPhotoPresenter(PhotoSelection photoSelection, Router router) {
|
||||
this.photoSelection = photoSelection;
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFirstViewAttach() {
|
||||
super.onFirstViewAttach();
|
||||
|
||||
getViewState().showPhotos(new int[] {
|
||||
R.drawable.ava_1,
|
||||
R.drawable.ava_2,
|
||||
R.drawable.ava_3,
|
||||
R.drawable.ava_4
|
||||
});
|
||||
}
|
||||
|
||||
public void onPhotoClick(int photoRes) {
|
||||
photoSelection.setSelectedPhoto(photoRes);
|
||||
router.exit();
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
router.exit();
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.animation.photos;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
|
||||
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy.class)
|
||||
public interface SelectPhotoView extends MvpView {
|
||||
void showPhotos(int[] resurceIds);
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.animation.profile;
|
||||
|
||||
import com.arellomobile.mvp.InjectViewState;
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
@InjectViewState
|
||||
public class ProfilePresenter extends MvpPresenter<ProfileView> {
|
||||
|
||||
private Router router;
|
||||
private PhotoSelection photoSelection;
|
||||
|
||||
public ProfilePresenter(PhotoSelection photoSelection, Router router) {
|
||||
this.photoSelection = photoSelection;
|
||||
this.router = router;
|
||||
|
||||
photoSelection.setListener(new PhotoSelection.Listener() {
|
||||
@Override
|
||||
public void onChange(int selectedPhoto) {
|
||||
updatePhoto();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFirstViewAttach() {
|
||||
super.onFirstViewAttach();
|
||||
updatePhoto();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
photoSelection.setListener(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void updatePhoto() {
|
||||
getViewState().showPhoto(photoSelection.getSelectedPhoto());
|
||||
}
|
||||
|
||||
public void onPhotoClicked() {
|
||||
router.navigateTo(new Screens.SelectPhotoScreen());
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
router.exit();
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.animation.profile;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
|
||||
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy.class)
|
||||
public interface ProfileView extends MvpView {
|
||||
void showPhoto(int resId);
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.bottom;
|
||||
|
||||
import com.arellomobile.mvp.InjectViewState;
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
@InjectViewState
|
||||
public class BottomNavigationPresenter extends MvpPresenter<BottomNavigationView> {
|
||||
private Router router;
|
||||
|
||||
public BottomNavigationPresenter(Router router) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
router.exit();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.bottom;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
|
||||
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
@StateStrategyType(AddToEndSingleStrategy.class)
|
||||
public interface BottomNavigationView extends MvpView {
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.bottom.forward;
|
||||
|
||||
import com.arellomobile.mvp.InjectViewState;
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
|
||||
@InjectViewState
|
||||
public class ForwardPresenter extends MvpPresenter<ForwardView> {
|
||||
private String container;
|
||||
private Router router;
|
||||
private int number;
|
||||
|
||||
public ForwardPresenter(String container, Router router, int number) {
|
||||
this.container = container;
|
||||
this.router = router;
|
||||
this.number = number;
|
||||
|
||||
getViewState().setChainText(createChain(number));
|
||||
}
|
||||
|
||||
private String createChain(int number) {
|
||||
String chain = "[0]";
|
||||
|
||||
for (int i = 0; i < number; i++) {
|
||||
chain += "➔" + (i + 1);
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
public void onForwardPressed() {
|
||||
router.navigateTo(new Screens.ForwardScreen(container, number + 1));
|
||||
}
|
||||
|
||||
public void onGithubPressed() {
|
||||
router.navigateTo(new Screens.GithubScreen());
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
router.exit();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.bottom.forward;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
|
||||
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy.class)
|
||||
public interface ForwardView extends MvpView {
|
||||
void setChainText(String chainText);
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.main;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.arellomobile.mvp.InjectViewState;
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
@InjectViewState
|
||||
public class SamplePresenter extends MvpPresenter<SampleView> {
|
||||
private Router router;
|
||||
private int screenNumber;
|
||||
private ScheduledExecutorService executorService;
|
||||
private ScheduledFuture<?> future;
|
||||
|
||||
public SamplePresenter(Router router, int screenNumber) {
|
||||
this.router = router;
|
||||
this.screenNumber = screenNumber;
|
||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
getViewState().setTitle("Screen " + screenNumber);
|
||||
}
|
||||
|
||||
public void onBackCommandClick() {
|
||||
router.exit();
|
||||
}
|
||||
|
||||
public void onForwardCommandClick() {
|
||||
router.navigateTo(new Screens.SampleScreen(screenNumber + 1));
|
||||
}
|
||||
|
||||
public void onReplaceCommandClick() {
|
||||
router.replaceScreen(new Screens.SampleScreen(screenNumber + 1));
|
||||
}
|
||||
|
||||
public void onNewChainCommandClick() {
|
||||
router.newChain(
|
||||
new Screens.SampleScreen(screenNumber + 1),
|
||||
new Screens.SampleScreen(screenNumber + 2),
|
||||
new Screens.SampleScreen(screenNumber + 3)
|
||||
);
|
||||
}
|
||||
|
||||
public void onFinishChainCommandClick() {
|
||||
router.finishChain();
|
||||
}
|
||||
|
||||
public void onNewRootCommandClick() {
|
||||
router.newRootScreen(new Screens.SampleScreen(screenNumber + 1));
|
||||
}
|
||||
|
||||
public void onForwardWithDelayCommandClick() {
|
||||
if (future != null) future.cancel(true);
|
||||
future = executorService.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//WARNING! Navigation must be only in UI thread.
|
||||
new Handler(Looper.getMainLooper()).post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
router.navigateTo(new Screens.SampleScreen(screenNumber + 1));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void onBackToCommandClick() {
|
||||
router.backTo(new Screens.SampleScreen(3));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.main;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
|
||||
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
@StateStrategyType(AddToEndSingleStrategy.class)
|
||||
public interface SampleView extends MvpView {
|
||||
void setTitle(String title);
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.start;
|
||||
|
||||
import com.arellomobile.mvp.MvpPresenter;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
|
||||
/**
|
||||
* Created by terrakok 21.11.16
|
||||
*/
|
||||
public class StartActivityPresenter extends MvpPresenter<StartActivityView> {
|
||||
private Router router;
|
||||
|
||||
public StartActivityPresenter(Router router) {
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
public void onOrdinaryPressed() {
|
||||
router.navigateTo(new Screens.MainScreen());
|
||||
}
|
||||
|
||||
public void onMultiPressed() {
|
||||
router.navigateTo(new Screens.BottomNavigationScreen());
|
||||
}
|
||||
|
||||
public void onResultWithAnimationPressed() {
|
||||
router.navigateTo(new Screens.ProfileScreen());
|
||||
}
|
||||
|
||||
public void onBackPressed() {
|
||||
router.exit();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.mvp.start;
|
||||
|
||||
import com.arellomobile.mvp.MvpView;
|
||||
|
||||
/**
|
||||
* Created by terrakok 21.11.16
|
||||
*/
|
||||
public interface StartActivityView extends MvpView {
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.subnavigation;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import ru.terrakok.cicerone.Cicerone;
|
||||
import ru.terrakok.cicerone.Router;
|
||||
|
||||
/**
|
||||
* Created by terrakok 27.11.16
|
||||
*/
|
||||
public class LocalCiceroneHolder {
|
||||
private HashMap<String, Cicerone<Router>> containers;
|
||||
|
||||
public LocalCiceroneHolder() {
|
||||
containers = new HashMap<>();
|
||||
}
|
||||
|
||||
public Cicerone<Router> getCicerone(String containerTag) {
|
||||
if (!containers.containsKey(containerTag)) {
|
||||
containers.put(containerTag, Cicerone.create());
|
||||
}
|
||||
return containers.get(containerTag);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.animations;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.transition.ChangeBounds;
|
||||
import android.view.View;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.NavigatorHolder;
|
||||
import ru.terrakok.cicerone.android.support.SupportAppNavigator;
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
import ru.terrakok.cicerone.commands.Forward;
|
||||
import ru.terrakok.cicerone.commands.Replace;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.photos.SelectPhotoFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.profile.ProfileFragment;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
public class ProfileActivity extends AppCompatActivity {
|
||||
public static final String PHOTO_TRANSITION = "photo_trasition";
|
||||
|
||||
@Inject
|
||||
NavigatorHolder navigatorHolder;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_container);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(new Command[]{new Replace(new Screens.ProfileInfoScreen())});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResumeFragments() {
|
||||
super.onResumeFragments();
|
||||
navigatorHolder.setNavigator(navigator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
navigatorHolder.removeNavigator();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private Navigator navigator = new SupportAppNavigator(this, R.id.container) {
|
||||
@Override
|
||||
protected void setupFragmentTransaction(Command command, Fragment currentFragment, Fragment nextFragment, FragmentTransaction fragmentTransaction) {
|
||||
if (command instanceof Forward
|
||||
&& currentFragment instanceof ProfileFragment
|
||||
&& nextFragment instanceof SelectPhotoFragment) {
|
||||
setupSharedElementForProfileToSelectPhoto(
|
||||
(ProfileFragment) currentFragment,
|
||||
(SelectPhotoFragment) nextFragment,
|
||||
fragmentTransaction
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void setupSharedElementForProfileToSelectPhoto(ProfileFragment profileFragment,
|
||||
SelectPhotoFragment selectPhotoFragment,
|
||||
FragmentTransaction fragmentTransaction) {
|
||||
ChangeBounds changeBounds = new ChangeBounds();
|
||||
selectPhotoFragment.setSharedElementEnterTransition(changeBounds);
|
||||
selectPhotoFragment.setSharedElementReturnTransition(changeBounds);
|
||||
profileFragment.setSharedElementEnterTransition(changeBounds);
|
||||
profileFragment.setSharedElementReturnTransition(changeBounds);
|
||||
|
||||
View view = profileFragment.getAvatarViewForAnimation();
|
||||
fragmentTransaction.addSharedElement(view, PHOTO_TRANSITION);
|
||||
selectPhotoFragment.setAnimationDestinationId((Integer) view.getTag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
|
||||
if (fragment != null
|
||||
&& fragment instanceof BackButtonListener
|
||||
&& ((BackButtonListener) fragment).onBackPressed()) {
|
||||
return;
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.animations.photos;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatFragment;
|
||||
import com.arellomobile.mvp.presenter.InjectPresenter;
|
||||
import com.arellomobile.mvp.presenter.ProvidePresenter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoPresenter;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.photos.SelectPhotoView;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
public class SelectPhotoFragment extends MvpAppCompatFragment implements SelectPhotoView, BackButtonListener {
|
||||
private static final String ARG_ANIM_DESTINATION = "arg_anim_dest";
|
||||
|
||||
private ImageView photo1;
|
||||
private ImageView photo2;
|
||||
private ImageView photo3;
|
||||
private ImageView photo4;
|
||||
|
||||
@Inject
|
||||
Router router;
|
||||
|
||||
@Inject
|
||||
PhotoSelection photoSelection;
|
||||
|
||||
@InjectPresenter
|
||||
SelectPhotoPresenter presenter;
|
||||
|
||||
public void setAnimationDestinationId(int resId) {
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments == null) arguments = new Bundle();
|
||||
arguments.putInt(ARG_ANIM_DESTINATION, resId);
|
||||
setArguments(arguments);
|
||||
}
|
||||
|
||||
private int getAnimationDestionationId() {
|
||||
return getArguments().getInt(ARG_ANIM_DESTINATION);
|
||||
}
|
||||
|
||||
@ProvidePresenter
|
||||
SelectPhotoPresenter providePresenter() {
|
||||
return new SelectPhotoPresenter(photoSelection, router);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_select_photo, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
photo1 = (ImageView) view.findViewById(R.id.select_image_1);
|
||||
photo2 = (ImageView) view.findViewById(R.id.select_image_2);
|
||||
photo3 = (ImageView) view.findViewById(R.id.select_image_3);
|
||||
photo4 = (ImageView) view.findViewById(R.id.select_image_4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
photo1.setOnClickListener(clickListener);
|
||||
photo2.setOnClickListener(clickListener);
|
||||
photo3.setOnClickListener(clickListener);
|
||||
photo4.setOnClickListener(clickListener);
|
||||
}
|
||||
|
||||
private View.OnClickListener clickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
photo1.setTransitionName(null);
|
||||
photo2.setTransitionName(null);
|
||||
photo3.setTransitionName(null);
|
||||
photo4.setTransitionName(null);
|
||||
v.setTransitionName(ProfileActivity.PHOTO_TRANSITION);
|
||||
presenter.onPhotoClick((Integer) v.getTag());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void showPhotos(int[] resurceIds) {
|
||||
if (resurceIds.length >= 4) {
|
||||
photo1.setImageResource(resurceIds[0]);
|
||||
photo2.setImageResource(resurceIds[1]);
|
||||
photo3.setImageResource(resurceIds[2]);
|
||||
photo4.setImageResource(resurceIds[3]);
|
||||
|
||||
photo1.setTag(resurceIds[0]);
|
||||
photo2.setTag(resurceIds[1]);
|
||||
photo3.setTag(resurceIds[2]);
|
||||
photo4.setTag(resurceIds[3]);
|
||||
|
||||
//for shared element animation
|
||||
int animRes = getAnimationDestionationId();
|
||||
photo1.setTransitionName(animRes == resurceIds[0] ? ProfileActivity.PHOTO_TRANSITION : null);
|
||||
photo2.setTransitionName(animRes == resurceIds[1] ? ProfileActivity.PHOTO_TRANSITION : null);
|
||||
photo3.setTransitionName(animRes == resurceIds[2] ? ProfileActivity.PHOTO_TRANSITION : null);
|
||||
photo4.setTransitionName(animRes == resurceIds[3] ? ProfileActivity.PHOTO_TRANSITION : null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
presenter.onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.animations.profile;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatFragment;
|
||||
import com.arellomobile.mvp.presenter.InjectPresenter;
|
||||
import com.arellomobile.mvp.presenter.ProvidePresenter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.profile.ProfilePresenter;
|
||||
import ru.terrakok.cicerone.sample.mvp.animation.profile.ProfileView;
|
||||
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
|
||||
*/
|
||||
|
||||
public class ProfileFragment extends MvpAppCompatFragment implements ProfileView, BackButtonListener {
|
||||
private ImageView avatar;
|
||||
|
||||
@Inject
|
||||
Router router;
|
||||
|
||||
@InjectPresenter
|
||||
ProfilePresenter presenter;
|
||||
|
||||
@Inject
|
||||
PhotoSelection photoSelection;
|
||||
|
||||
@ProvidePresenter
|
||||
ProfilePresenter providePresenter() {
|
||||
return new ProfilePresenter(photoSelection, router);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_profile, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
avatar = (ImageView) view.findViewById(R.id.avatar_imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
avatar.setTransitionName(ProfileActivity.PHOTO_TRANSITION);
|
||||
avatar.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
presenter.onPhotoClicked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showPhoto(int resId) {
|
||||
avatar.setImageResource(resId);
|
||||
|
||||
//for shared element animation
|
||||
avatar.setTag(resId);
|
||||
}
|
||||
|
||||
public View getAvatarViewForAnimation() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
presenter.onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-152
@@ -1,152 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.bottom;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatActivity;
|
||||
import com.arellomobile.mvp.presenter.InjectPresenter;
|
||||
import com.arellomobile.mvp.presenter.ProvidePresenter;
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
import ru.terrakok.cicerone.sample.mvp.bottom.BottomNavigationPresenter;
|
||||
import ru.terrakok.cicerone.sample.mvp.bottom.BottomNavigationView;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
public class BottomNavigationActivity extends MvpAppCompatActivity implements BottomNavigationView, RouterProvider {
|
||||
private BottomNavigationBar bottomNavigationBar;
|
||||
|
||||
@Inject
|
||||
Router router;
|
||||
|
||||
@InjectPresenter
|
||||
BottomNavigationPresenter presenter;
|
||||
|
||||
@ProvidePresenter
|
||||
public BottomNavigationPresenter createBottomNavigationPresenter() {
|
||||
return new BottomNavigationPresenter(router);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_bottom);
|
||||
bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.ab_bottom_navigation_bar);
|
||||
|
||||
initViews();
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
bottomNavigationBar.selectTab(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
bottomNavigationBar
|
||||
.addItem(new BottomNavigationItem(R.drawable.ic_android_white_24dp, R.string.tab_android))
|
||||
.addItem(new BottomNavigationItem(R.drawable.ic_bug_report_white_24dp, R.string.tab_bug))
|
||||
.addItem(new BottomNavigationItem(R.drawable.ic_pets_white_24dp, R.string.tab_dog))
|
||||
.initialise();
|
||||
bottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.OnTabSelectedListener() {
|
||||
@Override
|
||||
public void onTabSelected(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
selectTab("ANDROID");
|
||||
break;
|
||||
case 1:
|
||||
selectTab("BUG");
|
||||
break;
|
||||
case 2:
|
||||
selectTab("DOG");
|
||||
break;
|
||||
}
|
||||
bottomNavigationBar.selectTab(position, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(int position) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(int position) {
|
||||
onTabSelected(position);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void selectTab(String tab) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
Fragment currentFragment = null;
|
||||
List<Fragment> fragments = fm.getFragments();
|
||||
if (fragments != null) {
|
||||
for (Fragment f : fragments) {
|
||||
if (f.isVisible()) {
|
||||
currentFragment = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Fragment newFragment = fm.findFragmentByTag(tab);
|
||||
|
||||
if (currentFragment != null && newFragment != null && currentFragment == newFragment) return;
|
||||
|
||||
FragmentTransaction transaction = fm.beginTransaction();
|
||||
if (newFragment == null) {
|
||||
transaction.add(R.id.ab_container, new Screens.TabScreen(tab).getFragment(), tab);
|
||||
}
|
||||
|
||||
if (currentFragment != null) {
|
||||
transaction.hide(currentFragment);
|
||||
}
|
||||
|
||||
if (newFragment != null) {
|
||||
transaction.show(newFragment);
|
||||
}
|
||||
transaction.commitNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
Fragment fragment = null;
|
||||
List<Fragment> fragments = fm.getFragments();
|
||||
if (fragments != null) {
|
||||
for (Fragment f : fragments) {
|
||||
if (f.isVisible()) {
|
||||
fragment = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fragment != null
|
||||
&& fragment instanceof BackButtonListener
|
||||
&& ((BackButtonListener) fragment).onBackPressed()) {
|
||||
return;
|
||||
} else {
|
||||
presenter.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Router getRouter() {
|
||||
return router;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.bottom;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatFragment;
|
||||
import com.arellomobile.mvp.presenter.InjectPresenter;
|
||||
import com.arellomobile.mvp.presenter.ProvidePresenter;
|
||||
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.mvp.bottom.forward.ForwardPresenter;
|
||||
import ru.terrakok.cicerone.sample.mvp.bottom.forward.ForwardView;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
public class ForwardFragment extends MvpAppCompatFragment implements ForwardView, BackButtonListener {
|
||||
private static final String EXTRA_NAME = "extra_name";
|
||||
private static final String EXTRA_NUMBER = "extra_number";
|
||||
|
||||
private Toolbar toolbar;
|
||||
private TextView chainTV;
|
||||
private View forwardBt;
|
||||
private View githubBt;
|
||||
|
||||
@InjectPresenter
|
||||
ForwardPresenter presenter;
|
||||
|
||||
@ProvidePresenter
|
||||
ForwardPresenter provideForwardPresenter() {
|
||||
return new ForwardPresenter(
|
||||
getArguments().getString(EXTRA_NAME),
|
||||
((RouterProvider) getParentFragment()).getRouter(),
|
||||
getArguments().getInt(EXTRA_NUMBER)
|
||||
);
|
||||
}
|
||||
|
||||
public static ForwardFragment getNewInstance(String name, int number) {
|
||||
ForwardFragment fragment = new ForwardFragment();
|
||||
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putString(EXTRA_NAME, name);
|
||||
arguments.putInt(EXTRA_NUMBER, number);
|
||||
fragment.setArguments(arguments);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_forward, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
|
||||
chainTV = (TextView) view.findViewById(R.id.chain_text);
|
||||
forwardBt = view.findViewById(R.id.forward_button);
|
||||
githubBt = view.findViewById(R.id.github_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
toolbar.setTitle(getArguments().getString(EXTRA_NAME));
|
||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onBackPressed();
|
||||
}
|
||||
});
|
||||
forwardBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onForwardPressed();
|
||||
}
|
||||
});
|
||||
githubBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onGithubPressed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChainText(String chainText) {
|
||||
chainTV.setText(chainText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
presenter.onBackPressed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.bottom;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Cicerone;
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.android.support.SupportAppNavigator;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
import ru.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
public class TabContainerFragment extends Fragment implements RouterProvider, BackButtonListener {
|
||||
private static final String EXTRA_NAME = "tcf_extra_name";
|
||||
|
||||
private Navigator navigator;
|
||||
|
||||
@Inject
|
||||
LocalCiceroneHolder ciceroneHolder;
|
||||
|
||||
public static TabContainerFragment getNewInstance(String name) {
|
||||
TabContainerFragment fragment = new TabContainerFragment();
|
||||
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putString(EXTRA_NAME, name);
|
||||
fragment.setArguments(arguments);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private String getContainerName() {
|
||||
return getArguments().getString(EXTRA_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
private Cicerone<Router> getCicerone() {
|
||||
return ciceroneHolder.getCicerone(getContainerName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_tab_container, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
if (getChildFragmentManager().findFragmentById(R.id.ftc_container) == null) {
|
||||
getCicerone().getRouter().replaceScreen(new Screens.ForwardScreen(getContainerName(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
getCicerone().getNavigatorHolder().setNavigator(getNavigator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
getCicerone().getNavigatorHolder().removeNavigator();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private Navigator getNavigator() {
|
||||
if (navigator == null) {
|
||||
navigator = new SupportAppNavigator(getActivity(), getChildFragmentManager(), R.id.ftc_container);
|
||||
}
|
||||
return navigator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Router getRouter() {
|
||||
return getCicerone().getRouter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.ftc_container);
|
||||
if (fragment != null
|
||||
&& fragment instanceof BackButtonListener
|
||||
&& ((BackButtonListener) fragment).onBackPressed()) {
|
||||
return true;
|
||||
} else {
|
||||
((RouterProvider) getActivity()).getRouter().exit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.common;
|
||||
|
||||
/**
|
||||
* Created by terrakok 26.11.16
|
||||
*/
|
||||
public interface BackButtonListener {
|
||||
boolean onBackPressed();
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.common;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
|
||||
/**
|
||||
* Created by terrakok 25.11.16
|
||||
*/
|
||||
public interface RouterProvider {
|
||||
Router getRouter();
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.main;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Navigator;
|
||||
import ru.terrakok.cicerone.NavigatorHolder;
|
||||
import ru.terrakok.cicerone.android.support.SupportAppNavigator;
|
||||
import ru.terrakok.cicerone.commands.Command;
|
||||
import ru.terrakok.cicerone.commands.Replace;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.Screens;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class MainActivity extends MvpAppCompatActivity {
|
||||
private TextView screensSchemeTV;
|
||||
|
||||
@Inject
|
||||
NavigatorHolder navigatorHolder;
|
||||
|
||||
private Navigator navigator = new SupportAppNavigator(this, R.id.main_container) {
|
||||
@Override
|
||||
public void applyCommands(Command[] commands) {
|
||||
super.applyCommands(commands);
|
||||
getSupportFragmentManager().executePendingTransactions();
|
||||
printScreensScheme();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
screensSchemeTV = (TextView) findViewById(R.id.screens_scheme);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
navigator.applyCommands(new Command[]{new Replace(new Screens.SampleScreen(1))});
|
||||
} else {
|
||||
printScreensScheme();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResumeFragments() {
|
||||
super.onResumeFragments();
|
||||
navigatorHolder.setNavigator(navigator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
navigatorHolder.removeNavigator();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
|
||||
if (fragment != null
|
||||
&& fragment instanceof BackButtonListener
|
||||
&& ((BackButtonListener) fragment).onBackPressed()) {
|
||||
return;
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void printScreensScheme() {
|
||||
ArrayList<SampleFragment> fragments = new ArrayList<>();
|
||||
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
|
||||
if (fragment instanceof SampleFragment) {
|
||||
fragments.add((SampleFragment) fragment);
|
||||
}
|
||||
}
|
||||
Collections.sort(fragments, new Comparator<SampleFragment>() {
|
||||
@Override
|
||||
public int compare(SampleFragment f1, SampleFragment f2) {
|
||||
long t = f1.getCreationTime() - f2.getCreationTime();
|
||||
if (t > 0) return 1;
|
||||
else if (t < 0) return -1;
|
||||
else return 0;
|
||||
}
|
||||
});
|
||||
|
||||
ArrayList<Integer> keys = new ArrayList<>();
|
||||
for (SampleFragment fragment : fragments) {
|
||||
keys.add(fragment.getNumber());
|
||||
}
|
||||
screensSchemeTV.setText("Chain: " + keys.toString() + "");
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
package ru.terrakok.cicerone.sample.ui.main;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.arellomobile.mvp.MvpAppCompatFragment;
|
||||
import com.arellomobile.mvp.presenter.InjectPresenter;
|
||||
import com.arellomobile.mvp.presenter.ProvidePresenter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ru.terrakok.cicerone.Router;
|
||||
import ru.terrakok.cicerone.sample.R;
|
||||
import ru.terrakok.cicerone.sample.SampleApplication;
|
||||
import ru.terrakok.cicerone.sample.mvp.main.SamplePresenter;
|
||||
import ru.terrakok.cicerone.sample.mvp.main.SampleView;
|
||||
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
|
||||
|
||||
/**
|
||||
* Created by Konstantin Tckhovrebov (aka @terrakok)
|
||||
* on 11.10.16
|
||||
*/
|
||||
|
||||
public class SampleFragment extends MvpAppCompatFragment implements SampleView, BackButtonListener {
|
||||
private static final String EXTRA_NUMBER = "extra_number";
|
||||
private static final String EXTRA_TIME = "extra_time";
|
||||
|
||||
private Toolbar toolbar;
|
||||
private View backCommandBt;
|
||||
private View forwardCommandBt;
|
||||
private View replaceCommandBt;
|
||||
private View newChainCommandBt;
|
||||
private View newRootCommandBt;
|
||||
private View forwardWithDelayCommandBt;
|
||||
private View backToCommandBt;
|
||||
private View finishChainCommandBt;
|
||||
|
||||
@Inject
|
||||
Router router;
|
||||
|
||||
@InjectPresenter
|
||||
SamplePresenter presenter;
|
||||
|
||||
@ProvidePresenter
|
||||
public SamplePresenter createSamplePresenter() {
|
||||
return new SamplePresenter(router, getArguments().getInt(EXTRA_NUMBER));
|
||||
}
|
||||
|
||||
public static SampleFragment getNewInstance(int number) {
|
||||
SampleFragment fragment = new SampleFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(EXTRA_NUMBER, number);
|
||||
args.putLong(EXTRA_TIME, System.currentTimeMillis());
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return getArguments().getInt(EXTRA_NUMBER);
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return getArguments().getLong(EXTRA_TIME, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
SampleApplication.INSTANCE.getAppComponent().inject(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_sample, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
|
||||
backCommandBt = view.findViewById(R.id.back_command);
|
||||
forwardCommandBt = view.findViewById(R.id.forward_command);
|
||||
replaceCommandBt = view.findViewById(R.id.replace_command);
|
||||
newChainCommandBt = view.findViewById(R.id.new_chain_command);
|
||||
newRootCommandBt = view.findViewById(R.id.new_root_command);
|
||||
forwardWithDelayCommandBt = view.findViewById(R.id.forward_delay_command);
|
||||
backToCommandBt = view.findViewById(R.id.back_to_command);
|
||||
finishChainCommandBt = view.findViewById(R.id.finish_chain_command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onBackCommandClick();
|
||||
}
|
||||
});
|
||||
backCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onBackCommandClick();
|
||||
}
|
||||
});
|
||||
forwardCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onForwardCommandClick();
|
||||
}
|
||||
});
|
||||
replaceCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onReplaceCommandClick();
|
||||
}
|
||||
});
|
||||
newChainCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onNewChainCommandClick();
|
||||
}
|
||||
});
|
||||
newRootCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onNewRootCommandClick();
|
||||
}
|
||||
});
|
||||
forwardWithDelayCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onForwardWithDelayCommandClick();
|
||||
}
|
||||
});
|
||||
backToCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onBackToCommandClick();
|
||||
}
|
||||
});
|
||||
finishChainCommandBt.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
presenter.onFinishChainCommandClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
toolbar.setTitle(title);
|
||||
}
|
||||
|
||||
public boolean onBackPressed() {
|
||||
presenter.onBackCommandClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user