53 Commits

Author SHA1 Message Date
Konstantin Tskhovrebov 98b1ff265f Merge branch 'develop' 2018-01-08 13:40:26 +03:00
Konstantin Tskhovrebov 9464fae539 updated library version to 3.0.0 2018-01-08 13:24:26 +03:00
Konstantin db0061a416 Merge pull request #58 from Jeevuz/small_refactor_and_docs
Small refactor and docs
2017-12-27 11:31:04 +03:00
Konstantin 8b2b1e0137 Merge pull request #59 from Jeevuz/feature/screen_name_for_unexisting
Add a screen key to the backToUnexisting method.
2017-12-27 11:29:40 +03:00
Vasili Chyrvon 2ac7848bbb Add a screen key to the backToUnexisting method. 2017-12-25 00:41:43 +03:00
Vasili Chyrvon 29bc51db6f Add the licence file 2017-12-25 00:31:00 +03:00
Vasili Chyrvon e2f78e8185 Change readme 2017-12-25 00:27:43 +03:00
Vasili Chyrvon 3eb0302046 Made executeCommands package-private 2017-12-25 00:25:40 +03:00
Vasili Chyrvon 933fdd2715 Add readability improvements and javadocs. 2017-12-25 00:14:23 +03:00
Konstantin Tskhovrebov 143b144cbf updated gradle version 2017-12-23 00:47:17 +03:00
Konstantin Tskhovrebov eeb5d20b8c Merge branch 'feature/commands_batch' into develop 2017-12-23 00:28:56 +03:00
Konstantin Tskhovrebov fe79f30c25 removed blocking navigation (popBackStackImmediate) 2017-12-21 17:18:45 +03:00
Konstantin Tskhovrebov 0ee0df0bcc moved auth comment to file header for library package 2017-12-19 23:42:57 +03:00
Konstantin Tskhovrebov f665c263ee changed navigator logic for apply commands batch 2017-12-19 22:56:01 +03:00
Konstantin 835d6425be Merge pull request #51 from Jeevuz/feature/context_for_activity_intents
Add Context to the activity creation method.
2017-12-01 14:28:14 +03:00
Vasili Chyrvon 851535ab28 Fix signature of the createActivityIntent() method in the sample and readme. 2017-11-27 02:13:34 +03:00
Vasili Chyrvon e4bb13b025 Add Context to the activity creation method. 2017-11-24 14:45:11 +03:00
Konstantin 4e0f36d57a Merge pull request #49 from Jeevuz/check_activity_exists
Added check for the activity existence before opening.
2017-10-24 09:22:05 +03:00
Vasili Chyrvon 752c22c0b9 Added check for the activity existence before opening. 2017-10-23 17:25:23 +03:00
terrakok ced094ea55 Merge branch 'develop' 2017-08-21 23:02:21 +03:00
terrakok ecd214fa36 update readme.md for new version 2017-08-21 22:58:35 +03:00
Konstantin dcfe1a32b8 Merge pull request #41 from Jeevuz/small_javadocs_fix
Changed the javadocs of the animation allowing methods to look similar.
2017-08-02 11:02:52 +03:00
Vasili Chyrvon 4120ce878d Changed the javadocs of the animation allowing methods to look similar. 2017-08-02 01:28:34 +03:00
Konstantin c9ca3f76da Merge pull request #37 from Jeevuz/patch-2
Update FragmentNavigator.java
2017-08-01 23:10:45 +03:00
Konstantin 9a7b4421c2 Merge pull request #38 from Jeevuz/patch-3
Update SupportFragmentNavigator.java
2017-08-01 23:09:47 +03:00
Konstantin 81774d2d12 Merge pull request #39 from Popalay/feature/activity-animations
Feature/activity animations
2017-08-01 23:09:04 +03:00
Denis Nikiforov 30f2f736c0 Update docs 2017-07-29 22:12:40 +03:00
Denis Nikiforov 0f59f588b8 Rename setupActivityTransactionAnimation to createStartActivityOptions 2017-07-29 22:09:50 +03:00
Denis Nikiforov 68bc8c809c Create activity transition only if activity intent is not null 2017-07-28 12:34:26 +03:00
Denis Nikiforov 37d5818413 Add support of activity transitions 2017-07-27 16:04:40 +03:00
Vasili Chyrvon 7344926349 Update SupportFragmentNavigator.java
Small change in docs
2017-07-18 01:03:58 +03:00
Vasili Chyrvon 357f8d9670 Update FragmentNavigator.java
Small change in docs
2017-07-18 01:01:59 +03:00
terrakok d8266b02b8 Merge branch 'develop' 2017-07-18 00:51:17 +03:00
terrakok 7337901f19 updated version to 2.0.0 AND fixed java docs 2017-07-18 00:50:45 +03:00
terrakok 2e647aec0e updated README 2017-07-18 00:37:38 +03:00
terrakok e10725955b update README file for new version 2017-07-18 00:35:09 +03:00
terrakok f357e9db3a Merge branch 'feature/fragment_animations' into develop 2017-07-17 23:26:57 +03:00
terrakok 5ed9df5409 added javaDocs for new setupFragmentTransactionAnimation navigator method AND update sample 2017-07-17 23:20:13 +03:00
Konstantin Tskhovrebov b493cf5a7b fix after merge 2017-07-15 13:47:29 +03:00
Konstantin Tskhovrebov 2be8ac0db2 Merge branch 'develop' into feature/fragment_animations 2017-07-15 13:45:15 +03:00
Konstantin Tskhovrebov b317498d95 changed weak references on manual set/remove result listener for Router 2017-07-15 13:44:53 +03:00
Konstantin Tskhovrebov 230d7ba064 added sample for present fragment transaction animation and returning result from screen 2017-07-15 01:37:59 +03:00
Konstantin Tskhovrebov b5c2d20ea3 added next fragment instance to applyFragmentAnimations method 2017-07-15 01:35:19 +03:00
Konstantin Tskhovrebov 1647377fb1 Merge branch 'develop' into feature/fragment_animations 2017-07-14 22:43:43 +03:00
Konstantin f6b8c0bfe1 Merge pull request #36 from terrakok/feature/result_bus
Feature/result_bus
2017-07-12 12:55:18 +03:00
terrakok 5a4eeef49f added javaDocs 2017-07-12 01:16:55 +03:00
terrakok 340e659502 removed ResultData marker 2017-07-12 01:05:10 +03:00
terrakok f88ab56ee9 changed call ordering for exitWithResult method 2017-07-12 01:03:16 +03:00
terrakok 3e26610157 renamed requestCode -> resultCode 2017-07-12 01:01:45 +03:00
terrakok 906baccdfe Added the ability to apply animation to a fragment transaction 2017-07-05 00:36:34 +03:00
terrakok 72d100197a added simple "bus" to Router for listen result data 2017-07-04 23:57:27 +03:00
terrakok 10918fd759 added warning for bad sample code 2017-05-31 01:10:44 +03:00
terrakok c02d5a1ff8 optimized navigator backToRoot implementation 2017-05-31 01:04:04 +03:00
57 changed files with 1351 additions and 373 deletions
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2017 Konstantin Tskhovrebov (@terrakok)
and Vasili Chyrvon (@Jeevuz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+54 -37
View File
@@ -1,18 +1,18 @@
# Cicerone
[![jCenter](https://api.bintray.com/packages/terrakok/terramaven/cicerone/images/download.svg)](https://bintray.com/terrakok/terramaven/cicerone/_latestVersion)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Join the chat at https://gitter.im/terrakok/Cicerone](https://img.shields.io/badge/Gitter-Join%20Chat-brightred.svg?style=flat)](https://gitter.im/terrakok/Cicerone)
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Cicerone-green.svg?style=true)](https://android-arsenal.com/details/1/4700)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-250-lightgrey.svg)](http://androidweekly.net/issues/issue-250)
[![Telegram](https://img.shields.io/badge/Telegram-ENG-blue.svg)](https://t.me/Cicerone_ENG)
[![Telegram](https://img.shields.io/badge/Telegram-RUS-blue.svg)](https://t.me/Cicerone_RUS)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-250-green.svg)](http://androidweekly.net/issues/issue-250)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-271-green.svg)](http://androidweekly.net/issues/issue-271)
![](https://habrastorage.org/files/644/32e/9eb/64432e9eb3664723b3ee438449dab3b0.png)
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.
[Russian version readme](https://github.com/terrakok/Cicerone/blob/develop/README_RUS.md)
## Main advantages
+ is not tied to Fragments
+ not a framework
@@ -21,6 +21,11 @@ It was designed to be used with the MVP pattern (try [Moxy](https://github.com/A
+ functionality is simple to extend
+ suitable for Unit Testing
## Additional features
+ easy screen result subscription
+ predefined navigator ready for setup transition animation
**See the sample application**
## How to add
Add the dependency in your build.gradle:
```groovy
@@ -84,21 +89,21 @@ public class SamplePresenter extends Presenter<SampleView> {
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
CommandBuffer checks whether there are _"active"_ Navigator:
If yes, it passes the commands to the Navigator. Navigator will process them to achive the desired transition.
If no, then CommandBuffer saves the commands in a queue, and will apply them as soon as new _"active"_ Navigator will appear.
- 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
protected void executeCommand(Command command) {
if (navigator != null) {
navigator.applyCommand(command);
} else {
pendingCommands.add(command);
void executeCommands(Command[] commands) {
if (navigator != null) {
navigator.applyCommands(commands);
} else {
pendingCommands.add(commands);
}
}
}
```
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.
**Attention**: Use _onResumeFragments()_ with FragmentActivity ([more info](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
@@ -125,45 +130,54 @@ private Navigator navigator = new Navigator() {
## 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
+ Forward - Opens new screen
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ Back - Rolls back the last transition
+ Back - Rolls back the last transition
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
+ BackTo - Rolls back to the needed screen in the screens chain
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - Replaces the current screen
+ Replace - Replaces the current screen
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - Shows system message (Alert, Toast, Snack, etc.)
+ SystemMessage - Shows system message (Alert, Toast, Snack, etc.)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
## Predefined navigators
The library provides predefined navigators for _Fragments_ to use inside _Activity_.
To use, just provide it with the container and _FragmentManager_ and override few simple methods.
The library provides predefined navigators for _Fragments_ to use inside _Activity_.
To use, just provide it with the container and _FragmentManager_ and override few simple methods.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
private Navigator navigator = new SupportAppNavigator(this, R.id.container) {
@Override
protected Intent createActivityIntent(Context context, String screenKey, Object data) {
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
switch (screenKey) {
case Screens.PROFILE_SCREEN:
return new ProfileFragment();
case Screens.SELECT_PHOTO_SCREEN:
return SelectPhotoFragment.getNewInstance((int) data);
}
return null;
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
protected void setupFragmentTransactionAnimation(
Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
//setup animation
}
};
```
## Sample
To see how to add, initialize and use the library and predefined navigators check out the sample.
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
![](https://habrastorage.org/web/a94/d73/653/a94d736534694d9daa994e0c260fca28.gif)
![](https://habrastorage.org/web/6dd/a19/15c/6dda1915cdcf4f14bed16fcffb3fd938.gif)
![](https://habrastorage.org/web/a63/881/7f8/a638817f8bba49daacc4fa427987fabb.gif)
## Participants
+ idea and code - Konstantin Tskhovrebov (@terrakok)
@@ -171,7 +185,10 @@ To see how to add, initialize and use the library and predefined navigators chec
## License
```
The MIT License (MIT)
MIT License
Copyright (c) 2017 Konstantin Tskhovrebov (@terrakok)
and Vasili Chyrvon (@Jeevuz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -190,4 +207,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
```
+4
View File
@@ -1,5 +1,9 @@
# Cicerone
[![Telegram](https://img.shields.io/badge/Telegram-RUS-blue.svg)](https://t.me/Cicerone_RUS)
![](https://habrastorage.org/files/644/32e/9eb/64432e9eb3664723b3ee438449dab3b0.png)
Cicerone (_"чи-че-ро́-не"_ - устар. гид) - легкая библиотека для простой реализации навигации в андроид приложении.
Разработана для использования в MVP архитектуре (попробуйте [Moxy](https://github.com/Arello-Mobile/Moxy)), но легко встраивается в любые решения.
+1 -1
View File
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.android.tools.build:gradle:3.0.1'
// For the library uploading to the Bintray
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1'
+2 -2
View File
@@ -1,6 +1,6 @@
#Fri Mar 10 17:09:31 MSK 2017
#Sat Dec 23 00:31:04 MSK 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
+1 -1
View File
@@ -15,7 +15,7 @@ ext {
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
artifact = 'cicerone'
libraryVersion = '1.2.1'
libraryVersion = '3.0.0'
gitUrl = 'https://github.com/terrakok/Cicerone'
allLicenses = ['MIT']
}
@@ -1,12 +1,11 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* BaseRouter is an abstract class to implement high-level navigation.
* Extend it to add needed transition methods.
@@ -23,11 +22,11 @@ public abstract class BaseRouter {
}
/**
* Sends navigation command to {@link CommandBuffer}.
* Sends navigation command array to {@link CommandBuffer}.
*
* @param command navigation command to execute
* @param commands navigation command array to execute
*/
protected void executeCommand(Command command) {
commandBuffer.executeCommand(command);
protected void executeCommands(Command... commands) {
commandBuffer.executeCommands(commands);
}
}
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* 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.
@@ -1,3 +1,7 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone;
import java.util.LinkedList;
@@ -5,25 +9,20 @@ import java.util.Queue;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* 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<>();
private Queue<Command[]> pendingCommands = new LinkedList<>();
@Override
public void setNavigator(Navigator navigator) {
this.navigator = navigator;
while (!pendingCommands.isEmpty()) {
if (navigator != null) {
executeCommand(pendingCommands.poll());
executeCommands(pendingCommands.poll());
} else break;
}
}
@@ -34,15 +33,15 @@ class CommandBuffer implements NavigatorHolder {
}
/**
* Passes {@code command} to the {@link Navigator} if it available.
* Passes {@code commands} to the {@link Navigator} if it available.
* Else puts it to the pending commands queue to pass it later.
* @param command navigation command
* @param commands navigation command array
*/
public void executeCommand(Command command) {
void executeCommands(Command[] commands) {
if (navigator != null) {
navigator.applyCommand(command);
navigator.applyCommands(commands);
} else {
pendingCommands.add(command);
pendingCommands.add(commands);
}
}
}
@@ -1,12 +1,11 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone;
import ru.terrakok.cicerone.commands.Command;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* The low-level navigation interface.
* Navigator is the one who actually performs any transition.
@@ -16,7 +15,7 @@ public interface Navigator {
/**
* Performs transition described by the navigation command
*
* @param command the navigation command to apply
* @param commands the navigation command array to apply per single transaction
*/
void applyCommand(Command command);
void applyCommands(Command[] commands);
}
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone;
/**
* Navigator holder interface.
* Use it to connect a {@link Navigator} to the {@link Cicerone}.
@@ -1,15 +1,17 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone;
import java.util.HashMap;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
import ru.terrakok.cicerone.result.ResultListener;
/**
* Router is the class for high-level navigation.
@@ -19,10 +21,49 @@ import ru.terrakok.cicerone.commands.SystemMessage;
*/
public class Router extends BaseRouter {
private HashMap<Integer, ResultListener> resultListeners = new HashMap<>();
public Router() {
super();
}
/**
* Subscribe to the screen result.<br>
* <b>Note:</b> only one listener can subscribe to a unique resultCode!<br>
* You must call a <b>removeResultListener()</b> to avoid a memory leak.
*
* @param resultCode key for filter results
* @param listener result listener
*/
public void setResultListener(Integer resultCode, ResultListener listener) {
resultListeners.put(resultCode, listener);
}
/**
* Unsubscribe from the screen result.
*
* @param resultCode key for filter results
*/
public void removeResultListener(Integer resultCode) {
resultListeners.remove(resultCode);
}
/**
* Send result data to subscriber.
*
* @param resultCode result data key
* @param result result data
* @return TRUE if listener was notified and FALSE otherwise
*/
protected boolean sendResult(Integer resultCode, Object result) {
ResultListener resultListener = resultListeners.get(resultCode);
if (resultListener != null) {
resultListener.onResult(result);
return true;
}
return false;
}
/**
* Open new screen and add it to the screens chain.
*
@@ -39,7 +80,7 @@ public class Router extends BaseRouter {
* @param data initialisation parameters for the new screen
*/
public void navigateTo(String screenKey, Object data) {
executeCommand(new Forward(screenKey, data));
executeCommands(new Forward(screenKey, data));
}
/**
@@ -60,8 +101,10 @@ public class Router extends BaseRouter {
* @param data initialisation parameters for the new screen
*/
public void newScreenChain(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Forward(screenKey, data));
executeCommands(
new BackTo(null),
new Forward(screenKey, data)
);
}
/**
@@ -80,8 +123,10 @@ public class Router extends BaseRouter {
* @param data initialisation parameters for the root
*/
public void newRootScreen(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Replace(screenKey, data));
executeCommands(
new BackTo(null),
new Replace(screenKey, data)
);
}
/**
@@ -106,7 +151,7 @@ public class Router extends BaseRouter {
* @param data initialisation parameters for the new screen
*/
public void replaceScreen(String screenKey, Object data) {
executeCommand(new Replace(screenKey, data));
executeCommands(new Replace(screenKey, data));
}
/**
@@ -117,7 +162,7 @@ public class Router extends BaseRouter {
* @param screenKey screen key
*/
public void backTo(String screenKey) {
executeCommand(new BackTo(screenKey));
executeCommands(new BackTo(screenKey));
}
/**
@@ -125,8 +170,10 @@ public class Router extends BaseRouter {
* It's mostly used to finish the application or close a supplementary navigation chain.
*/
public void finishChain() {
executeCommand(new BackTo(null));
executeCommand(new Back());
executeCommands(
new BackTo(null),
new Back()
);
}
/**
@@ -135,7 +182,18 @@ public class Router extends BaseRouter {
* the processing of the {@link Back} command in a {@link Navigator} implementation.
*/
public void exit() {
executeCommand(new Back());
executeCommands(new Back());
}
/**
* Return to the previous screen in the chain and send result data.
*
* @param resultCode result data key
* @param result result data
*/
public void exitWithResult(Integer resultCode, Object result) {
exit();
sendResult(resultCode, result);
}
/**
@@ -144,8 +202,10 @@ public class Router extends BaseRouter {
* @param message message to show
*/
public void exitWithMessage(String message) {
executeCommand(new Back());
executeCommand(new SystemMessage(message));
executeCommands(
new Back(),
new SystemMessage(message)
);
}
/**
@@ -154,6 +214,6 @@ public class Router extends BaseRouter {
* @param message message to show
*/
public void showSystemMessage(String message) {
executeCommand(new SystemMessage(message));
executeCommands(new SystemMessage(message));
}
}
@@ -1,8 +1,14 @@
/*
* Created by Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
@@ -17,8 +23,6 @@ import ru.terrakok.cicerone.commands.Replace;
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class AppNavigator extends FragmentNavigator {
@@ -34,32 +38,61 @@ public abstract class AppNavigator extends FragmentNavigator {
this.activity = activity;
}
/**
* 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;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
protected void forward(Forward command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
// Start activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
} else {
super.forward(command);
}
}
// Use default fragments navigation
super.applyCommand(command);
@Override
protected void replace(Replace command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Replace activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
activity.finish();
} else {
super.replace(command);
}
}
private void checkAndStartActivity(String screenKey, Intent activityIntent, Bundle options) {
// Check if we can start activity
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(activityIntent, options);
} else {
unexistingActivity(screenKey, activityIntent);
}
}
/**
* Called when there is no activity to open {@code screenKey}.
*
* @param screenKey screen key
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(String screenKey, Intent activityIntent) {
// Do nothing by default
}
/**
@@ -68,11 +101,12 @@ public abstract class AppNavigator extends FragmentNavigator {
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param context
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
protected abstract Intent createActivityIntent(Context context, String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
@@ -1,7 +1,14 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.android;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import java.util.LinkedList;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
@@ -11,17 +18,12 @@ import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* {@link Navigator} implementation based on the fragments.
* <p>
* {@link BackTo} navigation command will return to the root if
* needed screen isn't found in the screens chain.
* To change this behavior override {@link #backToUnexisting()} method.
* To change this behavior override {@link #backToUnexisting(String)} method.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
@@ -30,6 +32,7 @@ import ru.terrakok.cicerone.commands.SystemMessage;
public abstract class FragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
private LinkedList<String> localStackCopy;
/**
* Creates FragmentNavigator.
@@ -42,74 +45,173 @@ public abstract class FragmentNavigator implements Navigator {
this.containerId = containerId;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
} else {
exit();
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
String key = ((BackTo) command).getScreenKey();
/**
* Override this method to setup custom fragment transaction animation.
*
* @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 setupFragmentTransactionAnimation(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
}
if (key == null) {
backToRoot();
} else {
boolean hasScreen = false;
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
if (key.equals(fragmentManager.getBackStackEntryAt(i).getName())) {
fragmentManager.popBackStackImmediate(key, 0);
hasScreen = true;
break;
}
}
if (!hasScreen) {
backToUnexisting();
}
}
@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) {
forward((Forward) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof BackTo) {
backTo((BackTo) command);
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
/**
* Performs {@link Forward} command transition
*/
protected void forward(Forward command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager.executePendingTransactions();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
}
/**
* Performs {@link Forward} command transition
*/
protected void back() {
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
} else {
exit();
}
}
/**
* Performs {@link Replace} command transition
*/
protected void replace(Replace command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.commit();
}
}
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
String key = command.getScreenKey();
if (key == null) {
backToRoot();
} else {
int index = localStackCopy.indexOf(key);
int size = localStackCopy.size();
if (index != -1) {
for (int i = 1; i < size - index; i++) {
localStackCopy.pop();
}
fragmentManager.popBackStack(key, 0);
} else {
backToUnexisting(command.getScreenKey());
}
}
}
private void backToRoot() {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
localStackCopy.clear();
}
/**
@@ -134,9 +236,11 @@ public abstract class FragmentNavigator implements Navigator {
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
* Called when we tried to back to some specific screen (via {@link BackTo} command),
* but didn't found it.
* @param screenKey screen key
*/
protected void backToUnexisting() {
protected void backToUnexisting(String screenKey) {
backToRoot();
}
@@ -147,4 +251,4 @@ public abstract class FragmentNavigator implements Navigator {
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
}
@@ -1,7 +1,13 @@
/*
* Created by Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
@@ -18,8 +24,6 @@ import ru.terrakok.cicerone.commands.Replace;
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class SupportAppNavigator extends SupportFragmentNavigator {
@@ -35,32 +39,61 @@ public abstract class SupportAppNavigator extends SupportFragmentNavigator {
this.activity = activity;
}
/**
* 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;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
protected void forward(Forward command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
// Start activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
} else {
super.forward(command);
}
}
// Use default fragments navigation
super.applyCommand(command);
@Override
protected void replace(Replace command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Replace activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
activity.finish();
} else {
super.replace(command);
}
}
private void checkAndStartActivity(String screenKey, Intent activityIntent, Bundle options) {
// Check if we can start activity
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(activityIntent, options);
} else {
unexistingActivity(screenKey, activityIntent);
}
}
/**
* Called when there is no activity to open {@code screenKey}.
*
* @param screenKey screen key
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(String screenKey, Intent activityIntent) {
// Do nothing by default
}
/**
@@ -73,7 +106,7 @@ public abstract class SupportAppNavigator extends SupportFragmentNavigator {
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
protected abstract Intent createActivityIntent(Context context, String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
@@ -1,7 +1,14 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.android;
import android.support.v4.app.Fragment;
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;
@@ -11,17 +18,12 @@ import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* {@link Navigator} implementation based on the support fragments.
* <p>
* {@link BackTo} navigation command will return to the root if
* needed screen isn't found in the screens chain.
* To change this behavior override {@link #backToUnexisting()} method.
* To change this behavior override {@link #backToUnexisting(String)} method.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
@@ -30,6 +32,7 @@ import ru.terrakok.cicerone.commands.SystemMessage;
public abstract class SupportFragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
protected LinkedList<String> localStackCopy;
/**
* Creates SupportFragmentNavigator.
@@ -42,74 +45,173 @@ public abstract class SupportFragmentNavigator implements Navigator {
this.containerId = containerId;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
} else {
exit();
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
String key = ((BackTo) command).getScreenKey();
/**
* Override this method to setup custom fragment transaction animation.
*
* @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 setupFragmentTransactionAnimation(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
}
if (key == null) {
backToRoot();
} else {
boolean hasScreen = false;
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
if (key.equals(fragmentManager.getBackStackEntryAt(i).getName())) {
fragmentManager.popBackStackImmediate(key, 0);
hasScreen = true;
break;
}
}
if (!hasScreen) {
backToUnexisting();
}
}
@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) {
forward((Forward) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof BackTo) {
backTo((BackTo) command);
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
/**
* Performs {@link Forward} command transition
*/
protected void forward(Forward command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager.executePendingTransactions();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
}
/**
* Performs {@link Back} command transition
*/
protected void back() {
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
} else {
exit();
}
}
/**
* Performs {@link Replace} command transition
*/
protected void replace(Replace command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.commit();
}
}
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
String key = command.getScreenKey();
if (key == null) {
backToRoot();
} else {
int index = localStackCopy.indexOf(key);
int size = localStackCopy.size();
if (index != -1) {
for (int i = 1; i < size - index; i++) {
localStackCopy.pop();
}
fragmentManager.popBackStack(key, 0);
} else {
backToUnexisting(command.getScreenKey());
}
}
}
private void backToRoot() {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
localStackCopy.clear();
}
/**
@@ -134,9 +236,11 @@ public abstract class SupportFragmentNavigator implements Navigator {
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
* Called when we tried to back to some specific screen (via {@link BackTo} command),
* but didn't found it.
* @param screenKey screen key
*/
protected void backToUnexisting() {
protected void backToUnexisting(String screenKey) {
backToRoot();
}
@@ -146,4 +250,4 @@ public abstract class SupportFragmentNavigator implements Navigator {
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
}
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Rolls back the last transition from the screens chain.
*/
@@ -1,12 +1,11 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
import ru.terrakok.cicerone.Navigator;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Rolls back 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}.
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* 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}.
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Opens new screen.
*/
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Replaces the current screen.
*/
@@ -1,10 +1,9 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Shows system message.
*/
@@ -0,0 +1,15 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.result;
public interface ResultListener {
/**
* Received result from screen.
*
* @param resultData
*/
void onResult(Object resultData);
}
@@ -2,6 +2,7 @@ package android.app;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
/***
* Created by laomo on 2016-11-21.
@@ -11,6 +12,10 @@ public class Activity extends Context {
throw new RuntimeException("Stub!");
}
public void startActivity(Intent intent, Bundle options) {
throw new RuntimeException("Stub!");
}
public FragmentManager getFragmentManager() {
throw new RuntimeException("Stub!");
}
@@ -4,6 +4,7 @@ package android.app;
* Created by laomo on 2016-11-21.
*/
public class FragmentManager {
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
@@ -17,11 +18,7 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
public void popBackStack(String name, int flags) {
throw new RuntimeException("Stub!");
}
@@ -33,6 +30,10 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public Fragment findFragmentById(int id) {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
@@ -0,0 +1,5 @@
package android.content;
public class ComponentName {
}
@@ -1,8 +1,11 @@
package android.content;
import android.content.pm.PackageManager;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Context {
public PackageManager getPackageManager() { throw new RuntimeException("Stub!"); }
}
@@ -1,9 +1,12 @@
package android.content;
import android.content.pm.PackageManager;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 22.02.17
*/
public class Intent {
public ComponentName resolveActivity(PackageManager pm) { throw new RuntimeException("Stub!"); }
}
@@ -0,0 +1,5 @@
package android.content.pm;
public class PackageManager {
}
@@ -6,6 +6,7 @@ package android.support.v4.app;
*/
public class FragmentManager {
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
@@ -19,11 +20,7 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
public void popBackStack(String name, int flags) {
throw new RuntimeException("Stub!");
}
@@ -35,6 +32,9 @@ public class FragmentManager {
throw new RuntimeException("Stub!");
}
public Fragment findFragmentById(int id) {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
+9 -9
View File
@@ -6,10 +6,10 @@ repositories {
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
buildToolsVersion '26.0.2'
defaultConfig {
minSdkVersion 16
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0.0"
@@ -35,23 +35,23 @@ ext {
dependencies {
// Support libraries
compile "com.android.support:appcompat-v7:$supportLibraryVersion"
compile "com.android.support:design:$supportLibraryVersion"
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:design:$supportLibraryVersion"
//MVP Moxy
compile "com.arello-mobile:moxy:$moxyVersion"
compile "com.arello-mobile:moxy-app-compat:$moxyVersion"
provided "com.arello-mobile:moxy-compiler:$moxyVersion"
implementation "com.arello-mobile:moxy:$moxyVersion"
implementation "com.arello-mobile:moxy-app-compat:$moxyVersion"
annotationProcessor "com.arello-mobile:moxy-compiler:$moxyVersion"
//Cicerone
compile project(':library')
//DI
compile "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
//Bottom Navigation Bar
compile ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
implementation ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
exclude group: "com.android.support", module: "design"
}
}
+1
View File
@@ -20,5 +20,6 @@
<activity android:name=".ui.main.MainActivity"/>
<activity android:name=".ui.bottom.BottomNavigationActivity"/>
<activity android:name=".ui.animations.ProfileActivity"/>
</application>
</manifest>
@@ -18,4 +18,7 @@ public class Screens {
public static final String FORWARD_SCREEN = "forward screen";
public static final String GITHUB_SCREEN = "github screen";
public static final String PROFILE_SCREEN = "profile screen";
public static final String SELECT_PHOTO_SCREEN = "select photo screen";
}
@@ -5,6 +5,9 @@ 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.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;
@@ -31,4 +34,10 @@ public interface AppComponent {
void inject(BottomNavigationActivity activity);
void inject(TabContainerFragment fragment);
void inject(ProfileFragment fragment);
void inject(SelectPhotoFragment fragment);
void inject(ProfileActivity activity);
}
@@ -0,0 +1,42 @@
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;
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@InjectViewState
public class SelectPhotoPresenter extends MvpPresenter<SelectPhotoView> {
private Router router;
private final int RESULT_CODE;
public SelectPhotoPresenter(Router router, int resultCode) {
this.router = router;
RESULT_CODE = resultCode;
}
@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) {
router.exitWithResult(RESULT_CODE, photoRes);
}
public void onBackPressed() {
router.exit();
}
}
@@ -0,0 +1,14 @@
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);
}
@@ -0,0 +1,57 @@
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.result.ResultListener;
import ru.terrakok.cicerone.sample.Screens;
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
@InjectViewState
public class ProfilePresenter extends MvpPresenter<ProfileView> {
private static final int PHOTO_RESULT_CODE = 42;
private Router router;
private int currentPhoto;
public ProfilePresenter(int defaultPhoto, Router router) {
currentPhoto = defaultPhoto;
this.router = router;
router.setResultListener(PHOTO_RESULT_CODE, new ResultListener() {
@Override
public void onResult(Object resultData) {
currentPhoto = (int) resultData;
updatePhoto();
}
});
}
@Override
protected void onFirstViewAttach() {
super.onFirstViewAttach();
updatePhoto();
}
@Override
public void onDestroy() {
router.removeResultListener(PHOTO_RESULT_CODE);
super.onDestroy();
}
private void updatePhoto() {
getViewState().showPhoto(currentPhoto);
}
public void onPhotoClicked() {
router.navigateTo(Screens.SELECT_PHOTO_SCREEN, PHOTO_RESULT_CODE);
}
public void onBackPressed() {
router.exit();
}
}
@@ -0,0 +1,14 @@
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);
}
@@ -60,6 +60,7 @@ public class SamplePresenter extends MvpPresenter<SampleView> {
future = executorService.schedule(new Runnable() {
@Override
public void run() {
//WARNING! Navigation must be only in UI thread. this method works only for sample :)
router.navigateTo(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
}
}, 5, TimeUnit.SECONDS);
@@ -23,6 +23,10 @@ public class StartActivityPresenter extends MvpPresenter<StartActivityView> {
router.navigateTo(Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN);
}
public void onResultWithAnimationPressed() {
router.navigateTo(Screens.PROFILE_SCREEN);
}
public void onBackPressed() {
router.exit();
}
@@ -0,0 +1,118 @@
package ru.terrakok.cicerone.sample.ui.animations;
import android.content.Context;
import android.content.Intent;
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.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(Screens.PROFILE_SCREEN, null)});
}
}
@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 Intent createActivityIntent(Context context, String screenKey, Object data) {
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
switch (screenKey) {
case Screens.PROFILE_SCREEN:
return new ProfileFragment();
case Screens.SELECT_PHOTO_SCREEN:
return SelectPhotoFragment.getNewInstance((int) data);
}
return null;
}
@Override
protected void setupFragmentTransactionAnimation(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();
}
}
}
@@ -0,0 +1,135 @@
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.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_RESULT_CODE = "arg_result_code";
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;
@InjectPresenter
SelectPhotoPresenter presenter;
public static SelectPhotoFragment getNewInstance(int resultCode) {
SelectPhotoFragment fragment = new SelectPhotoFragment();
Bundle args = new Bundle();
args.putInt(ARG_RESULT_CODE, resultCode);
fragment.setArguments(args);
return fragment;
}
public void setAnimationDestinationId(int resId) {
Bundle arguments = getArguments();
arguments.putInt(ARG_ANIM_DESTINATION, resId);
setArguments(arguments);
}
private int getAnimationDestionationId() {
return getArguments().getInt(ARG_ANIM_DESTINATION);
}
@ProvidePresenter
SelectPhotoPresenter providePresenter() {
return new SelectPhotoPresenter(router, getArguments().getInt(ARG_RESULT_CODE));
}
@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;
}
}
@@ -0,0 +1,89 @@
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.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;
@ProvidePresenter
ProfilePresenter providePresenter() {
return new ProfilePresenter(R.drawable.ava_1, 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;
}
}
@@ -155,7 +155,11 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
public void applyCommands(Command[] commands) {
for (Command command : commands) applyCommand(command);
}
private void applyCommand(Command command) {
if (command instanceof Back) {
finish();
} else if (command instanceof SystemMessage) {
@@ -1,5 +1,6 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -89,7 +90,7 @@ public class TabContainerFragment extends Fragment implements RouterProvider, Ba
navigator = new SupportAppNavigator(getActivity(), getChildFragmentManager(), R.id.ftc_container) {
@Override
protected Intent createActivityIntent(String screenKey, Object data) {
protected Intent createActivityIntent(Context context, String screenKey, Object data) {
if (screenKey.equals(Screens.GITHUB_SCREEN)) {
return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"));
}
@@ -10,6 +10,8 @@ import com.arellomobile.mvp.MvpAppCompatActivity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
@@ -17,10 +19,7 @@ import javax.inject.Inject;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.android.SupportFragmentNavigator;
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;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
@@ -58,9 +57,10 @@ public class MainActivity extends MvpAppCompatActivity {
}
@Override
public void applyCommand(Command command) {
super.applyCommand(command);
updateScreenNames(command);
public void applyCommands(Command[] commands) {
super.applyCommands(commands);
getSupportFragmentManager().executePendingTransactions();
printScreensScheme();
}
};
@@ -73,7 +73,7 @@ public class MainActivity extends MvpAppCompatActivity {
screensSchemeTV = (TextView) findViewById(R.id.screens_scheme);
if (savedInstanceState == null) {
navigator.applyCommand(new Replace(Screens.SAMPLE_SCREEN, 1));
navigator.applyCommands(new Command[]{new Replace(Screens.SAMPLE_SCREEN, 1)});
} else {
screenNames = (List<String>) savedInstanceState.getSerializable(STATE_SCREEN_NAMES);
printScreensScheme();
@@ -110,35 +110,22 @@ public class MainActivity extends MvpAppCompatActivity {
outState.putSerializable(STATE_SCREEN_NAMES, (Serializable) screenNames);
}
private void updateScreenNames(Command command) {
if (command instanceof Back) {
if (screenNames.size() > 0) {
screenNames.remove(screenNames.size() - 1);
}
} else if (command instanceof Forward) {
int i = (int) ((Forward) command).getTransitionData();
screenNames.add(i + "");
} else if (command instanceof Replace) {
int i = (int) ((Replace) command).getTransitionData();
if (screenNames.size() > 0) {
screenNames.remove(screenNames.size() - 1);
}
screenNames.add(i + "");
} else if (command instanceof BackTo) {
screenNames = new ArrayList<>(screenNames.subList(0, getSupportFragmentManager().getBackStackEntryCount() + 1));
}
printScreensScheme();
}
private void printScreensScheme() {
String str = "";
for (String name : screenNames) {
if (!str.isEmpty()) {
str += "" + name;
} else {
str = "[" + name + "]";
ArrayList<Integer> keys = new ArrayList<>();
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof SampleFragment) {
keys.add(((SampleFragment) fragment).getNumber());
}
}
screensSchemeTV.setText("Chain: " + str + "");
Collections.sort(keys, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
screensSchemeTV.setText("Chain: " + keys.toString() + "");
}
}
@@ -59,6 +59,10 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
return fragment;
}
public int getNumber() {
return getArguments().getInt(EXTRA_NUMBER);
}
@Override
public void onCreate(Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
@@ -25,6 +25,7 @@ import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
import ru.terrakok.cicerone.sample.mvp.start.StartActivityPresenter;
import ru.terrakok.cicerone.sample.mvp.start.StartActivityView;
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
@@ -68,6 +69,12 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
presenter.onMultiPressed();
}
});
findViewById(R.id.result_and_anim_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onResultWithAnimationPressed();
}
});
}
@Override
@@ -87,9 +94,15 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
presenter.onBackPressed();
}
//Sample fully custom navigator:
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
public void applyCommands(Command[] commands) {
for (Command command : commands) applyCommand(command);
}
private void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Replace) {
@@ -114,6 +127,9 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, BottomNavigationActivity.class));
break;
case Screens.PROFILE_SCREEN:
startActivity(new Intent(StartActivity.this, ProfileActivity.class));
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
@@ -125,6 +141,7 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
case Screens.START_ACTIVITY_SCREEN:
case Screens.MAIN_ACTIVITY_SCREEN:
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
case Screens.PROFILE_SCREEN:
forward(new Forward(command.getScreenKey(), command.getTransitionData()));
finish();
break;
Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
@@ -36,4 +36,11 @@
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/multi_nav"/>
<Button
android:id="@+id/result_and_anim_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/result_and_anim_nav"/>
</LinearLayout>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/avatar_imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:layout_margin="40dp"
android:scaleType="centerCrop"
android:src="@drawable/ava_1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="24dp"
android:gravity="center"
android:text="Click on image for select other photo"
android:textSize="16sp"/>
</LinearLayout>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/select_image_1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/select_image_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/select_image_3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"/>
<ImageView
android:id="@+id/select_image_4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"/>
</LinearLayout>
+1
View File
@@ -3,6 +3,7 @@
<string name="cicerone_description">Cicerone is a lightweight library that makes the navigation in an Android app easy.</string>
<string name="ordinary_nav">Ordinary navigation</string>
<string name="multi_nav">Multi navigation</string>
<string name="result_and_anim_nav">Resulting and animation sample</string>
<string name="tab_android">Android</string>
<string name="tab_bug">Bug</string>