104 Commits

Author SHA1 Message Date
Konstantin Tskhovrebov 95f29e0143 Merge branch 'develop' 2018-10-03 23:13:07 +03:00
Konstantin Tskhovrebov dd4fdb5b83 Update version to 4.0.2. 2018-10-03 23:11:43 +03:00
Konstantin Tskhovrebov 5ea14dc30c Merge branch 'master' into develop 2018-10-03 23:11:16 +03:00
Konstantin 3da51271a1 Merge pull request #82 from qwert2603/patch-1
Fix error in Router.java
2018-10-03 23:05:59 +03:00
Alexander Zhdanov e2a7493549 Fix error in Router.java
I make call:
router.newRootChain(new ScreenOne(), new ScreenTwo());
and get a exception 
        java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
        at ru.terrakok.cicerone.Router.newRootChain(Router.java:91)
        at ...
I suppose, iteration bound must be equal to length of array "screens" (not "commands").
2018-10-03 17:56:05 +07:00
Konstantin Tskhovrebov f03ea7f60e Merge branch 'develop' 2018-10-01 22:59:26 +03:00
Konstantin Tskhovrebov 9919f6a73f Update version to 4.0.1. 2018-10-01 22:57:51 +03:00
Konstantin Tskhovrebov 6ff618272d Fix newRootChain command. 2018-10-01 22:49:25 +03:00
Konstantin Tskhovrebov 301f3f3db8 Merge branch 'develop' 2018-09-27 22:15:56 +03:00
Konstantin Tskhovrebov de961bcd24 Update version to 4.0.0. 2018-09-27 22:11:02 +03:00
Konstantin Tskhovrebov bccb7ffdce Add newRootChain method to Router. 2018-09-27 22:10:20 +03:00
Konstantin Tskhovrebov b82196ce8c Update readme and fix java docs. 2018-09-27 22:03:58 +03:00
Konstantin Tskhovrebov 1d733906c8 Add media for readme. 2018-09-27 21:57:08 +03:00
Konstantin Tskhovrebov 39305091b9 Update readme for new version. 2018-09-27 18:03:12 +03:00
Konstantin Tskhovrebov 8cbd987c7c Add media for documentation. 2018-09-27 17:11:52 +03:00
Konstantin Tskhovrebov 6b9b8ba992 Update gradle plugin. 2018-09-27 16:21:56 +03:00
Konstantin Tskhovrebov ef05b43b0d Add java docs for public methods. 2018-09-27 16:21:22 +03:00
Konstantin Tskhovrebov 7e316fa7ef Delete unused code. 2018-09-27 16:20:41 +03:00
Konstantin Tskhovrebov f7f45ad493 Change screen default id to "getCanonicalName". 2018-09-27 16:19:53 +03:00
Konstantin Tskhovrebov 62520744cb Fix "back to root" crash. 2018-09-22 21:31:48 +03:00
Konstantin Tskhovrebov 4a09e0083c Merge branch 'feature/screen_chain' into develop 2018-09-22 21:23:50 +03:00
Konstantin Tskhovrebov 74e9681a48 Add router method for opening screen chain. 2018-09-22 21:23:33 +03:00
Konstantin Tskhovrebov 3ef18015ee Fix library android dependencies. 2018-09-22 21:22:33 +03:00
Konstantin Tskhovrebov 163fc90fcd Merge branch 'feature/result_changes' into develop 2018-09-22 20:45:05 +03:00
Konstantin Tskhovrebov a920505915 Update sample for manual result listening. 2018-09-22 20:43:40 +03:00
Konstantin Tskhovrebov 88427b0271 Change pure android stubs on android source dependency. 2018-09-22 20:43:08 +03:00
Konstantin Tskhovrebov 3b8954b993 Remove result listener from Router. 2018-09-22 20:42:22 +03:00
Konstantin Tskhovrebov d9fd28fb2c Merge branch 'feature/screen_class' into develop 2018-09-20 15:29:10 +03:00
Konstantin Tskhovrebov f2977d599d Update sample for new library version. 2018-09-20 15:27:42 +03:00
Konstantin Tskhovrebov ccc8dd626c Update gradle plugin. 2018-09-20 12:48:51 +03:00
Konstantin Tskhovrebov c9f0b413cb Remove FragmentNavigator and create common AppNavigator. 2018-09-20 12:48:32 +03:00
Konstantin Tskhovrebov 47f5197f90 Add screen class (contains key and creation logic). 2018-09-09 02:51:03 +03:00
Konstantin Tskhovrebov d0b4e5c119 Update gradle plugin. 2018-09-09 01:11:56 +03:00
Konstantin f036217eb0 Merge pull request #69 from Aleksander3007/changed_pop
Changed pop() to removeLast().
2018-07-12 12:24:20 +03:00
aaermakov 29fcd3c834 Changed pop() to removeLast(). 2018-06-19 09:15:11 +03:00
Konstantin Tskhovrebov 92600e69b2 rename setupFragmentTransactionAnimation to setupFragmentTransaction. because it can be used not for only animations 2018-06-06 16:06:30 +03:00
Konstantin Tskhovrebov 9f41ec0d8f remove unused field MainActivity.screenNames 2018-06-06 14:30:25 +03:00
Konstantin Tskhovrebov 56fb9e82e9 fix: switch on main thread for forward command with delay 2018-06-06 14:28:56 +03:00
Konstantin Tskhovrebov 96c1ec0a25 fix invalid sorting for chain view on main activity 2018-06-06 14:27:52 +03:00
Konstantin Tskhovrebov d90f4ab3a1 Merge branch 'feature/remove_system_msg' into develop 2018-06-06 14:26:03 +03:00
Konstantin Tskhovrebov 07a956be3a remove SystemMessage command 2018-06-06 14:09:00 +03:00
Konstantin ad68431834 Update README.md 2018-01-21 14:53:35 +03:00
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
terrakok 2f6f4457b7 Merge branch 'develop' 2017-03-29 01:00:04 +03:00
terrakok d59e20269c updated version to 1.2.1 2017-03-29 00:56:55 +03:00
Konstantin 77c5645eb1 Merge pull request #26 from electrolobzik/develop
added hint for onResumeFragments() method to the readme
2017-03-28 12:55:51 +03:00
Roman Chernyak 8fef3a9e0b Moved comment from code block to the text area. 2017-03-28 12:51:58 +03:00
Roman Chernyak 53dd652942 Fixed code formatting in readme 2017-03-28 12:43:36 +03:00
Roman Chernyak edcf6ea1d0 - Removed extra text in readme
- Fixed BottomNavigationActivity to match readme
2017-03-28 12:31:46 +03:00
Roman Chernyak 25b3050d98 added hint for onResumeFragments() method to the readme and corrected the sample to correspond the readme 2017-03-28 00:10:49 +03:00
Konstantin 1f09d9ddd8 Merge pull request #25 from Jeevuz/throw-if-unknown
Throw if unknown
2017-03-27 22:46:56 +03:00
jeevuz 66ab47b3cd Throw in the unknownScreen() method. 2017-03-27 22:34:48 +03:00
80 changed files with 1730 additions and 1430 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.
+51 -55
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,12 +21,20 @@ 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
+ opening several screens inside single call (for example: deeplink)
+ 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
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:X.X'
compile 'ru.terrakok.cicerone:cicerone:X.X.X'
}
```
@@ -59,7 +67,7 @@ public class SampleApplication extends MvpApplication {
```
## How it works?
![](https://habrastorage.org/files/4df/45d/973/4df45d9733fc4ee0a2f0be933de475b1.png)
<img src="https://github.com/terrakok/Cicerone/raw/develop/media/CiceroneDiagram.png" alt="drawing" width="800"/>
Presenter calls navigation method of Router.
@@ -76,29 +84,31 @@ public class SamplePresenter extends Presenter<SampleView> {
}
public void onForwardCommandClick() {
router.navigateTo("Some screen");
router.navigateTo(new SomeScreen());
}
}
```
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
CommandBuffer checks whether there are _"active"_ Navigator:
If yes, it passes the commands to the Navigator. Navigator will process them to achive the desired transition.
If no, then CommandBuffer saves the commands in a queue, and will apply them as soon as new _"active"_ Navigator will appear.
CommandBuffer checks whether there are _"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
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()))
```java
@Override
@@ -115,53 +125,36 @@ protected void onPause() {
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
//implement commands logic
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
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ 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
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - Replaces the current screen
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - Shows system message (Alert, Toast, Snack, etc.)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
+ Forward - Opens new screen
![](https://github.com/terrakok/Cicerone/raw/develop/media/forward_img.png)
+ Back - Rolls back the last transition
![](https://github.com/terrakok/Cicerone/raw/develop/media/back_img.png)
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://github.com/terrakok/Cicerone/raw/develop/media/backTo_img.png)
+ Replace - Replaces the current screen
![](https://github.com/terrakok/Cicerone/raw/develop/media/replace_img.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_ and _Activity_.
To use, just provide it with the container and _FragmentManager_.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
}
};
private Navigator navigator = new SupportAppNavigator(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)
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
![](https://github.com/terrakok/Cicerone/raw/develop/media/navigation.gif)
![](https://github.com/terrakok/Cicerone/raw/develop/media/insta_tabs.gif)
![](https://github.com/terrakok/Cicerone/raw/develop/media/animations.gif)
## Participants
+ idea and code - Konstantin Tskhovrebov (@terrakok)
@@ -169,7 +162,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
-172
View File
@@ -1,172 +0,0 @@
# Cicerone
Cicerone (_"чи-че-ро́-не"_ - устар. гид) - легкая библиотека для простой реализации навигации в андроид приложении.
Разработана для использования в MVP архитектуре (попробуйте [Moxy](https://github.com/Arello-Mobile/Moxy)), но легко встраивается в любые решения.
## Основные преимущества
+ не завязана на Fragment'ы
+ не фреймворк
+ короткие вызовы навигации (никаких билдеров)
+ lifecycle-безопасна!
+ простое расширение функционала
+ приспособлена для Unit тестов
## Как подключить?
Добавьте в build.gradle зависимость:
```groovy
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:X.X'
}
```
И инициализируйте библиотеку, например, так:
```java
public class SampleApplication extends MvpApplication {
public static SampleApplication INSTANCE;
private Cicerone<Router> cicerone;
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
initCicerone();
}
private void initCicerone() {
cicerone = Cicerone.create();
}
public NavigatorHolder getNavigatorHolder() {
return cicerone.getNavigatorHolder();
}
public Router getRouter() {
return cicerone.getRouter();
}
}
```
## Как это работает?
![](https://habrastorage.org/files/4df/45d/973/4df45d9733fc4ee0a2f0be933de475b1.png)
Presenter вызывает у Router метод навигации.
```java
public class SamplePresenter extends Presenter<SampleView> {
private Router router;
public SamplePresenter() {
router = SampleApplication.INSTANCE.getRouter();
}
public void onBackCommandClick() {
router.exit();
}
public void onForwardCommandClick() {
router.navigateTo("Some screen");
}
}
```
Router - класс, который реализует вызванный метод навигации набором Command и отправляет их в CommandBuffer.
CommandBuffer проверяет есть ли _"активный"_ Navigator.
Если да, то на нем вызываются необходимые команды для осуществления запрошенного перехода.
Если нет, то команды добавляются в очередь, которая будет применена, как только появится _"активный"_ Navigator.
```java
protected void executeCommand(Command command) {
if (navigator != null) {
navigator.applyCommand(command);
} else {
pendingCommands.add(command);
}
}
```
Navigator - (например) анонимный класс внутри Activity, который реализует команды навигации.
Activity предоставляет свой Navigator для CommandBuffer в методе _onResume_ и очищает в _onPause_
```java
@Override
protected void onResume() {
super.onResume();
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator);
}
@Override
protected void onPause() {
super.onPause();
SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator();
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
//implements commands logic
}
};
```
Навигатор не обязана быть в Activity. Он может быть и внутри Fragment'а, который переключает внутри себя View.
## Команды навигатора
Для большинства задач предоставленных в библиотеке команд должно хватить, но их всегда можно дополнить собственными!
+ Forward - переход на новый экран
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ Back - возврат на предыдущий экран
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
+ BackTo - возврат к конкретному экрану в цепочке, либо на корневой, если указанный экран не найден
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - замена текущего экрана на новый
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - показ системного сообщения (Alert, Toast, Snack и тд)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
## Готовые навигаторы
Библиотека предоставляет готовые навигаторы для _Activity_.
Чтобы их использовать, достаточно указать контейнер и передать _FragmentManager_.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
}
};
```
## Sample
Работу библиотеки, готовых навигаторов и другое можно посмотреть в _sample_ приложении.
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
## Участники
+ идея и реализация - Константин Цховребов (@terrakok)
+ архитектурные советы, документация и публикация - Василий Чирвон (@Jeevuz)
## Лицензия
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+3 -1
View File
@@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.android.tools.build:gradle:3.3.0-alpha12'
// For the library uploading to the Bintray
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1'
@@ -16,6 +17,7 @@ buildscript {
allprojects {
repositories {
jcenter()
google()
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
#Fri Mar 10 17:09:31 MSK 2017
#Thu Sep 27 15:42:22 MSK 2018
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.10.1-all.zip
+2 -1
View File
@@ -7,6 +7,7 @@ targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly project(':stub-android')
compileOnly 'com.google.android:android:4.1.1.4'
}
ext {
@@ -15,7 +16,7 @@ ext {
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
artifact = 'cicerone'
libraryVersion = '1.2'
libraryVersion = '4.0.2'
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,14 @@
/*
* 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;
import ru.terrakok.cicerone.commands.SystemMessage;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 12.10.16
*/
/**
* Router is the class for high-level navigation.
@@ -26,98 +25,73 @@ public class Router extends BaseRouter {
/**
* Open new screen and add it to the screens chain.
*
* @param screenKey screen key
* @param screen screen
*/
public void navigateTo(String screenKey) {
navigateTo(screenKey, null);
}
/**
* Open new screen and add it to screens chain.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void navigateTo(String screenKey, Object data) {
executeCommand(new Forward(screenKey, data));
}
/**
* Clear the current screens chain and start new one
* by opening a new screen right after the root.
*
* @param screenKey screen key
*/
public void newScreenChain(String screenKey) {
newScreenChain(screenKey, null);
}
/**
* Clear the current screens chain and start new one
* by opening a new screen right after the root.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void newScreenChain(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Forward(screenKey, data));
public void navigateTo(Screen screen) {
executeCommands(new Forward(screen));
}
/**
* Clear all screens and open new one as root.
*
* @param screenKey screen key
* @param screen screen
*/
public void newRootScreen(String screenKey) {
newRootScreen(screenKey, null);
}
/**
* Clear all screens and open new one as root.
*
* @param screenKey screen key
* @param data initialisation parameters for the root
*/
public void newRootScreen(String screenKey, Object data) {
executeCommand(new BackTo(null));
executeCommand(new Replace(screenKey, data));
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 back you will return to the previous screen
* so by going fragmentBack you will return to the previous screen
* and not to the replaced one.
*
* @param screenKey screen key
* @param screen screen
*/
public void replaceScreen(String screenKey) {
replaceScreen(screenKey, null);
public void replaceScreen(Screen screen) {
executeCommands(new Replace(screen));
}
/**
* Replace current screen.
* By replacing the screen, you alters the backstack,
* so by going back you will return to the previous screen
* and not to the replaced one.
*
* @param screenKey screen key
* @param data initialisation parameters for the new screen
*/
public void replaceScreen(String screenKey, Object data) {
executeCommand(new Replace(screenKey, data));
}
/**
* Return back to the needed screen from the chain.
* 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 screenKey screen key
* @param screen screen
*/
public void backTo(String screenKey) {
executeCommand(new BackTo(screenKey));
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);
}
/**
@@ -125,8 +99,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,25 +111,7 @@ 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 show system message.
*
* @param message message to show
*/
public void exitWithMessage(String message) {
executeCommand(new Back());
executeCommand(new SystemMessage(message));
}
/**
* Show system message.
*
* @param message message to show
*/
public void showSystemMessage(String message) {
executeCommand(new SystemMessage(message));
}
}
@@ -0,0 +1,12 @@
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,88 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link FragmentNavigator} to allow
* open new or replace current activity.
* <p>
* 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 {
private Activity activity;
public AppNavigator(Activity activity, int containerId) {
super(activity.getFragmentManager(), containerId);
this.activity = activity;
}
public AppNavigator(Activity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.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;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @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);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -1,149 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Fragment;
import android.app.FragmentManager;
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;
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.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
* </p>
*/
public abstract class FragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
/**
* Creates FragmentNavigator.
*
* @param fragmentManager fragment manager
* @param containerId id of the fragments container layout
*/
public FragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
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();
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();
}
}
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
}
fragmentManager.executePendingTransactions();
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @return instantiated fragment for the passed screen key
*/
protected abstract Fragment createFragment(String screenKey, Object data);
/**
* Shows system message.
*
* @param message message to show
*/
protected abstract void showSystemMessage(String message);
/**
* Called when we try to back from the root.
*/
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
*/
protected void backToUnexisting() {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
}
}
@@ -1,89 +0,0 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link SupportFragmentNavigator} to allow
* open new or replace current activity.
* <p>
* 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 {
private Activity activity;
public SupportAppNavigator(FragmentActivity activity, int containerId) {
super(activity.getSupportFragmentManager(), containerId);
this.activity = activity;
}
public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.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;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @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);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -1,148 +0,0 @@
package ru.terrakok.cicerone.android;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
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;
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.
* </p>
* <p>
* {@link Back} command will call {@link #exit()} method if current screen is the root.
* </p>
*/
public abstract class SupportFragmentNavigator implements Navigator {
private FragmentManager fragmentManager;
private int containerId;
/**
* Creates SupportFragmentNavigator.
*
* @param fragmentManager support fragment manager
* @param containerId id of the fragments container layout
*/
public SupportFragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
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();
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();
}
}
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
private void backToRoot() {
for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
fragmentManager.popBackStack();
}
fragmentManager.executePendingTransactions();
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @return instantiated fragment for the passed screen key
*/
protected abstract Fragment createFragment(String screenKey, Object data);
/**
* Shows system message.
*
* @param message message to show
*/
protected abstract void showSystemMessage(String message);
/**
* Called when we try to back from the root.
*/
protected abstract void exit();
/**
* Called when we tried to back to some specific screen, but didn't found it.
*/
protected void backToUnexisting() {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
}
}
@@ -0,0 +1,280 @@
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());
}
}
@@ -0,0 +1,23 @@
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;
}
}
@@ -0,0 +1,281 @@
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());
}
}
@@ -0,0 +1,23 @@
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,12 +1,11 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Rolls back the last transition from the screens chain.
* Rolls fragmentBack the last transition from the screens chain.
*/
public class Back implements Command {
@@ -1,30 +1,30 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.Screen;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Rolls back to the needed screen from the screens chain.
* 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 String screenKey;
private Screen screen;
/**
* Creates a {@link BackTo} navigation command.
*
* @param screenKey screen key
* @param screen screen
*/
public BackTo(String screenKey) {
this.screenKey = screenKey;
public BackTo(Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
public Screen getScreen() {
return 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;
/**
* Navigation command describes screens transition.
* that can be processed by {@link ru.terrakok.cicerone.Navigator}.
@@ -1,33 +1,27 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
import ru.terrakok.cicerone.Screen;
/**
* Opens new screen.
*/
public class Forward implements Command {
private String screenKey;
private Object transitionData;
private Screen screen;
/**
* Creates a {@link Forward} navigation command.
*
* @param screenKey screen key
* @param transitionData initial data
* @param screen screen
*/
public Forward(String screenKey, Object transitionData) {
this.screenKey = screenKey;
this.transitionData = transitionData;
public Forward(Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
public Screen getScreen() {
return screen;
}
}
@@ -1,33 +1,27 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
import ru.terrakok.cicerone.Screen;
/**
* Replaces the current screen.
*/
public class Replace implements Command {
private String screenKey;
private Object transitionData;
private Screen screen;
/**
* Creates a {@link Replace} navigation command.
*
* @param screenKey screen key
* @param transitionData initial data
* @param screen screen
*/
public Replace(String screenKey, Object transitionData) {
this.screenKey = screenKey;
this.transitionData = transitionData;
public Replace(Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
public Screen getScreen() {
return screen;
}
}
@@ -1,26 +0,0 @@
package ru.terrakok.cicerone.commands;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
/**
* Shows system message.
*/
public class SystemMessage implements Command {
private String message;
/**
* Creates a {@link SystemMessage} command.
*
* @param message message text
*/
public SystemMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
+4
View File
@@ -2,3 +2,7 @@ apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly 'com.google.android:android:4.1.1.4'
}
@@ -1,21 +0,0 @@
package android.app;
import android.content.Context;
import android.content.Intent;
/***
* Created by laomo on 2016-11-21.
*/
public class Activity extends Context {
public void startActivity(Intent intent) {
throw new RuntimeException("Stub!");
}
public FragmentManager getFragmentManager() {
throw new RuntimeException("Stub!");
}
public void finish() {
throw new RuntimeException("Stub!");
}
}
@@ -1,36 +0,0 @@
package android.app;
import android.os.Bundle;
/***
* Created by laomo on 2016-11-21.
*/
public class Fragment {
public void onCreate(Bundle savedInstanceState) {
throw new RuntimeException("Stub!");
}
public void onStart() {
throw new RuntimeException("Stub!");
}
public void onDestroyView() {
throw new RuntimeException("Stub!");
}
public void onDestroy() {
throw new RuntimeException("Stub!");
}
public void onSaveInstanceState(Bundle outState) {
throw new RuntimeException("Stub!");
}
final public boolean isRemoving() {
throw new RuntimeException("Stub!");
}
public final Activity getActivity() {
throw new RuntimeException("Stub!");
}
}
@@ -1,41 +0,0 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
public class FragmentManager {
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
}
public boolean executePendingTransactions() {
throw new RuntimeException("Stub!");
}
public void popBackStack() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
throw new RuntimeException("Stub!");
}
public int getBackStackEntryCount() {
throw new RuntimeException("Stub!");
}
public BackStackEntry getBackStackEntryAt(int index) {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
String getName();
}
}
@@ -1,18 +0,0 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
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!");
}
}
@@ -1,8 +0,0 @@
package android.content;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Context {
}
@@ -1,9 +0,0 @@
package android.content;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 22.02.17
*/
public class Intent {
}
@@ -1,7 +0,0 @@
package android.os;
/***
* Created by laomo on 2016-11-21.
*/
public class Bundle {
}
@@ -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();
@@ -1,20 +0,0 @@
package android.widget;
import android.content.Context;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Toast {
public static final int LENGTH_SHORT = 0;
public void show() {
throw new RuntimeException("Stub!");
}
public static Toast makeText(Context context, CharSequence text, int duration) {
throw new RuntimeException("Stub!");
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 754 KiB

+1
View File
@@ -0,0 +1 @@
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:62.0) Gecko/20100101 Firefox/62.0" version="9.1.8" editor="www.draw.io" type="device"><diagram id="7e88c67f-9661-f316-02ff-f99b9ebf4599" name="Page-1">7Vpbc6M2FP41nu4+JMPFYPsxdpLtw26bNjvt7lNHBgHqYkSF8GV/fY9A4iZwnBon0zbxTEY+Erp859O54Ym92uw/MJRGn6iP44ll+PuJfTuxrNnChP9CcCgF07lTCkJG/FJk1oJH8h1LoSGlOfFx1hrIKY05SdtCjyYJ9nhLhhiju/awgMbtVVMUYk3w6KFYl/5OfB6VUsswjLrjR0zCiHd71sj7FjKaJ3LBiWUHxV/ZvUFqMjk+i5BPdw2RfTexV4xSXrY2+xWOBbYKt/K5+4HeauMMJ/yUByy3fGKL4hyrLRcb4weFhjhPKodhxvG+TwdorYYb+h7M6mTAGEw3mLMDDNm3tS7J4kiu7GrkFwreqAH61JZCJLUdVjPXB4aGPPPA+Z2nz1+oE4sHjIm93EWE48cUeaJ3B/QHWcQ3sMCtCc2MM/oNr2hMWfG0bRR/VY/ikxgbkDhujJREATlNeN8MgzpoYl1qVAf7ZeDsQdONYYWlT7bQDEXzgeEMtouZ6oNZG93n4d/B1Hfw3J/26WVurW3XlWg/ytXMsyBW5q/NZ1vdiIYGTKtPAyMowNYU8CvNC6jPArUFkQ4ywvPA6wPZ9eZ4HZxL3X5cLeMFcTVNDUHsg8+QXynjEQ1pguK7WrpsY9zAE+8J/yLE14789lUOAizYodElvn6VT/2JOT9IZ4lyTkFUr/uR0lSOK3cqtvcE0nAcmjMPt+4uRyzEvMEmXR8Mx4iTbXv6c9Cdaqxd0c0GJf4yD4LXJ29EGfkOsyA1peAxgXDhJiZhAjIusB+F47bTJrk57SG520PySniOHuaXILnZpHjN6i9Nxtd9Fya5rZN8+kIk1yMNSXLlBtdMucBfcgxjurqAWDEVTe8QE4CcPU39dambj+tKUIWnP+ccZhm4E9gEvznruxMLd2YjdySyT40O2Rc62fu4Ph+B6nrc+xPakhBx+urmZgxo3dd0lov/vBmZ6mZkQB+jm5GZRtx7hsINbPcTSiAvZbo5EZQ2VsBLvOcXJTc4a9frJbc/W6zPz2EGyP2iEbbKVi8ZCZpHIkHj8ux2dXbPXojdpp5B3niwAuEwzb1iOjR/I3j3oS5MNLTxjAQxocIDLn2URYVy6gRepelWH5lXU/EZjAM76Xs1egzqO4un48PKPzapb40RH5p6oK6sjTh0UQ1S53b/ymmZztvGvfg0RSrTl5ZKCZLSBePP9F3mMYyT940Swbr7GMjKVQfqBoW5O1KckdrvIQSS+vTKMoWu6A3x/eJi95Gtfdlb5LokNUy74/JnJ1pFawxm9NQRnaVXBrjZxLn9F2qnqruNXiGynKejs+mlNKUHEG+aGra37itqajFobSNTWT1V3qtMYt2lhKkSQAgo0MjEXmGrVzHeFu9tpNklNBEWHMWxGAFRBbAAC6NOA9FMsUdQXNh4yRUt0GxsI72AWc6AHyQJQeDU3z4X0c6VNRS9UjhyEBfvWCJgG07+GbPGIJPttLjUZ58XF0p3FR+PcqlTdDuFUo+QL+OsRQqDBDWnitTDpzhLfuBFvIh8AUhQyFu8exeTAF95By/G76/fqPVcanVeopk9YaHpXIhbZg+3OprxcratgMCJfyPe0grPEKMsI56eZQ4DdUI6c/Ttl9NzxZTs5HxGrvBASRH2Km8xbWth1kG3TLLkQzXA+jz2QJSvJipTM20iQBUdGsNSMSA7sl+3y5rW22JolDPWNKggPY0Zejb3P2VGV6HdbOxUZnQIVt3zkYnRtSbzkXmhv6bUvVGjHnvEE4GpTuqZVYYJPimP+R9rum9mnQZHa1E537fGXpmtMTEq3EAtA7M+mLmWqytXiMrYCu+xl0uvVoZOZJMC3buBVu0xi19YwCWAcyRF3LVG3Itwdt04enOpQfGbP1QlQqcda/WVSS7mD/uqJJeweo0SoSjNtIrg5sJSggfMCBxCJGC3zzKW7RJgWZp7LQPqTvtd1fN9a7eCppgytm/trmOdZUPha/2rsHJ4/dM7++5v</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

+9 -10
View File
@@ -6,10 +6,9 @@ repositories {
android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
minSdkVersion 16
minSdkVersion 21
targetSdkVersion 25
versionCode 1
versionName "1.0.0"
@@ -35,23 +34,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')
implementation 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>
@@ -1,21 +1,115 @@
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 String SAMPLE_SCREEN = "sample_screen_";
public static final String START_ACTIVITY_SCREEN = "start activity screen";
public static final String MAIN_ACTIVITY_SCREEN = "main activity screen";
public static final class SampleScreen extends SupportAppScreen {
private final int number;
public static final String BOTTOM_NAVIGATION_ACTIVITY_SCREEN = "bna screen";
public SampleScreen(int number) {
this.number = number;
this.screenKey = getClass().getSimpleName() + "_" + number;
}
public static final String ANDROID_SCREEN = "android screen";
public static final String BUG_SCREEN = "bug screen";
public static final String DOG_SCREEN = "dog screen";
@Override
public Fragment getFragment() {
return SampleFragment.getNewInstance(number);
}
}
public static final String FORWARD_SCREEN = "forward screen";
public static final String GITHUB_SCREEN = "github screen";
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();
}
}
}
@@ -5,6 +5,10 @@ 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;
@@ -18,7 +22,8 @@ import ru.terrakok.cicerone.sample.ui.start.StartActivity;
@Singleton
@Component(modules = {
NavigationModule.class,
LocalNavigationModule.class
LocalNavigationModule.class,
PhotoSelectionModule.class
})
public interface AppComponent {
@@ -31,4 +36,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,23 @@
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;
}
}
@@ -0,0 +1,32 @@
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);
}
}
@@ -0,0 +1,44 @@
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();
}
}
@@ -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,55 @@
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();
}
}
@@ -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);
}
@@ -4,7 +4,6 @@ import com.arellomobile.mvp.InjectViewState;
import com.arellomobile.mvp.MvpPresenter;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.Screens;
/**
* Created by terrakok 25.11.16
@@ -17,21 +16,6 @@ public class BottomNavigationPresenter extends MvpPresenter<BottomNavigationView
this.router = router;
}
public void onTabAndroidClick() {
getViewState().highlightTab(BottomNavigationView.ANDROID_TAB_POSITION);
router.replaceScreen(Screens.ANDROID_SCREEN);
}
public void onTabBugClick() {
getViewState().highlightTab(BottomNavigationView.BUG_TAB_POSITION);
router.replaceScreen(Screens.BUG_SCREEN);
}
public void onTabDogClick() {
getViewState().highlightTab(BottomNavigationView.DOG_TAB_POSITION);
router.replaceScreen(Screens.DOG_SCREEN);
}
public void onBackPressed() {
router.exit();
}
@@ -9,9 +9,4 @@ import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
*/
@StateStrategyType(AddToEndSingleStrategy.class)
public interface BottomNavigationView extends MvpView {
int ANDROID_TAB_POSITION = 0;
int BUG_TAB_POSITION = 1;
int DOG_TAB_POSITION = 2;
void highlightTab(int position);
}
@@ -12,10 +12,12 @@ import ru.terrakok.cicerone.sample.Screens;
@InjectViewState
public class ForwardPresenter extends MvpPresenter<ForwardView> {
private String container;
private Router router;
private int number;
public ForwardPresenter(Router router, int number) {
public ForwardPresenter(String container, Router router, int number) {
this.container = container;
this.router = router;
this.number = number;
@@ -33,11 +35,11 @@ public class ForwardPresenter extends MvpPresenter<ForwardView> {
}
public void onForwardPressed() {
router.navigateTo(Screens.FORWARD_SCREEN, number + 1);
router.navigateTo(new Screens.ForwardScreen(container, number + 1));
}
public void onGithubPressed() {
router.navigateTo(Screens.GITHUB_SCREEN);
router.navigateTo(new Screens.GithubScreen());
}
public void onBackPressed() {
@@ -1,5 +1,8 @@
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;
@@ -36,23 +39,27 @@ public class SamplePresenter extends MvpPresenter<SampleView> {
}
public void onForwardCommandClick() {
router.navigateTo(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
router.navigateTo(new Screens.SampleScreen(screenNumber + 1));
}
public void onReplaceCommandClick() {
router.replaceScreen(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
router.replaceScreen(new Screens.SampleScreen(screenNumber + 1));
}
public void onNewChainCommandClick() {
router.newScreenChain(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
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(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
}
public void onBackWithMessageCommandClick() {
router.exitWithMessage("Exit from 'Screen " + screenNumber + "'");
router.newRootScreen(new Screens.SampleScreen(screenNumber + 1));
}
public void onForwardWithDelayCommandClick() {
@@ -60,12 +67,20 @@ public class SamplePresenter extends MvpPresenter<SampleView> {
future = executorService.schedule(new Runnable() {
@Override
public void run() {
router.navigateTo(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
//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(Screens.SAMPLE_SCREEN + 3);
router.backTo(new Screens.SampleScreen(3));
}
}
@@ -16,11 +16,15 @@ public class StartActivityPresenter extends MvpPresenter<StartActivityView> {
}
public void onOrdinaryPressed() {
router.navigateTo(Screens.MAIN_ACTIVITY_SCREEN);
router.navigateTo(new Screens.MainScreen());
}
public void onMultiPressed() {
router.navigateTo(Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN);
router.navigateTo(new Screens.BottomNavigationScreen());
}
public void onResultWithAnimationPressed() {
router.navigateTo(new Screens.ProfileScreen());
}
public void onBackPressed() {
@@ -0,0 +1,100 @@
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();
}
}
}
@@ -0,0 +1,131 @@
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;
}
}
@@ -0,0 +1,93 @@
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;
}
}
@@ -3,7 +3,7 @@ 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.widget.Toast;
import android.support.v4.app.FragmentTransaction;
import com.arellomobile.mvp.MvpAppCompatActivity;
import com.arellomobile.mvp.presenter.InjectPresenter;
@@ -11,15 +11,11 @@ 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.Navigator;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
@@ -33,16 +29,10 @@ import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
*/
public class BottomNavigationActivity extends MvpAppCompatActivity implements BottomNavigationView, RouterProvider {
private BottomNavigationBar bottomNavigationBar;
private TabContainerFragment androidTabFragment;
private TabContainerFragment bugTabFragment;
private TabContainerFragment dogTabFragment;
@Inject
Router router;
@Inject
NavigatorHolder navigatorHolder;
@InjectPresenter
BottomNavigationPresenter presenter;
@@ -60,10 +50,9 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.ab_bottom_navigation_bar);
initViews();
initContainers();
if (savedInstanceState == null) {
bottomNavigationBar.selectTab(ANDROID_TAB_POSITION, true);
bottomNavigationBar.selectTab(0, true);
}
}
@@ -77,16 +66,17 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
@Override
public void onTabSelected(int position) {
switch (position) {
case ANDROID_TAB_POSITION:
presenter.onTabAndroidClick();
case 0:
selectTab("ANDROID");
break;
case BUG_TAB_POSITION:
presenter.onTabBugClick();
case 1:
selectTab("BUG");
break;
case DOG_TAB_POSITION:
presenter.onTabDogClick();
case 2:
selectTab("DOG");
break;
}
bottomNavigationBar.selectTab(position, false);
}
@Override
@@ -102,48 +92,50 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
}
private void initContainers() {
private void selectTab(String tab) {
FragmentManager fm = getSupportFragmentManager();
androidTabFragment = (TabContainerFragment) fm.findFragmentByTag("ANDROID");
if (androidTabFragment == null) {
androidTabFragment = TabContainerFragment.getNewInstance("ANDROID");
fm.beginTransaction()
.add(R.id.ab_container, androidTabFragment, "ANDROID")
.detach(androidTabFragment).commitNow();
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);
}
bugTabFragment = (TabContainerFragment) fm.findFragmentByTag("BUG");
if (bugTabFragment == null) {
bugTabFragment = TabContainerFragment.getNewInstance("BUG");
fm.beginTransaction()
.add(R.id.ab_container, bugTabFragment, "BUG")
.detach(bugTabFragment).commitNow();
if (currentFragment != null) {
transaction.hide(currentFragment);
}
dogTabFragment = (TabContainerFragment) fm.findFragmentByTag("DOG");
if (dogTabFragment == null) {
dogTabFragment = TabContainerFragment.getNewInstance("DOG");
fm.beginTransaction()
.add(R.id.ab_container, dogTabFragment, "DOG")
.detach(dogTabFragment).commitNow();
if (newFragment != null) {
transaction.show(newFragment);
}
}
@Override
protected void onResume() {
super.onResume();
navigatorHolder.setNavigator(navigator);
}
@Override
protected void onPause() {
navigatorHolder.removeNavigator();
super.onPause();
transaction.commitNow();
}
@Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.ab_container);
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()) {
@@ -153,48 +145,6 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
}
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
if (command instanceof Back) {
finish();
} else if (command instanceof SystemMessage) {
Toast.makeText(BottomNavigationActivity.this, ((SystemMessage) command).getMessage(), Toast.LENGTH_SHORT).show();
} else if (command instanceof Replace) {
FragmentManager fm = getSupportFragmentManager();
switch (((Replace) command).getScreenKey()) {
case Screens.ANDROID_SCREEN:
fm.beginTransaction()
.detach(bugTabFragment)
.detach(dogTabFragment)
.attach(androidTabFragment)
.commitNow();
break;
case Screens.BUG_SCREEN:
fm.beginTransaction()
.detach(androidTabFragment)
.detach(dogTabFragment)
.attach(bugTabFragment)
.commitNow();
break;
case Screens.DOG_SCREEN:
fm.beginTransaction()
.detach(androidTabFragment)
.detach(bugTabFragment)
.attach(dogTabFragment)
.commitNow();
break;
}
}
}
};
@Override
public void highlightTab(int position) {
bottomNavigationBar.selectTab(position, false);
}
@Override
public Router getRouter() {
return router;
@@ -36,6 +36,7 @@ public class ForwardFragment extends MvpAppCompatFragment implements ForwardView
@ProvidePresenter
ForwardPresenter provideForwardPresenter() {
return new ForwardPresenter(
getArguments().getString(EXTRA_NAME),
((RouterProvider) getParentFragment()).getRouter(),
getArguments().getInt(EXTRA_NUMBER)
);
@@ -1,7 +1,5 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
@@ -14,7 +12,7 @@ import javax.inject.Inject;
import ru.terrakok.cicerone.Cicerone;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.android.SupportAppNavigator;
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;
@@ -68,7 +66,7 @@ public class TabContainerFragment extends Fragment implements RouterProvider, Ba
super.onActivityCreated(savedInstanceState);
if (getChildFragmentManager().findFragmentById(R.id.ftc_container) == null) {
getCicerone().getRouter().replaceScreen(Screens.FORWARD_SCREEN, 0);
getCicerone().getRouter().replaceScreen(new Screens.ForwardScreen(getContainerName(), 0));
}
}
@@ -86,29 +84,7 @@ public class TabContainerFragment extends Fragment implements RouterProvider, Ba
private Navigator getNavigator() {
if (navigator == null) {
navigator = new SupportAppNavigator(getActivity(), getChildFragmentManager(), R.id.ftc_container) {
@Override
protected Intent createActivityIntent(String screenKey, Object data) {
if (screenKey.equals(Screens.GITHUB_SCREEN)) {
return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"));
}
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
if (screenKey.equals(Screens.FORWARD_SCREEN)) {
return ForwardFragment.getNewInstance(getContainerName(), (int) data);
}
return null;
}
@Override
protected void exit() {
((RouterProvider) getActivity()).getRouter().exit();
}
};
navigator = new SupportAppNavigator(getActivity(), getChildFragmentManager(), R.id.ftc_container);
}
return navigator;
}
@@ -4,23 +4,19 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.widget.TextView;
import android.widget.Toast;
import com.arellomobile.mvp.MvpAppCompatActivity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
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.SupportFragmentNavigator;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
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;
@@ -33,34 +29,17 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
*/
public class MainActivity extends MvpAppCompatActivity {
private static final String STATE_SCREEN_NAMES = "state_screen_names";
private List<String> screenNames = new ArrayList<>();
private TextView screensSchemeTV;
@Inject
NavigatorHolder navigatorHolder;
private Navigator navigator = new SupportFragmentNavigator(getSupportFragmentManager(), R.id.main_container) {
private Navigator navigator = new SupportAppNavigator(this, R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
return SampleFragment.getNewInstance((int) data);
}
@Override
protected void showSystemMessage(String message) {
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
finish();
}
@Override
public void applyCommand(Command command) {
super.applyCommand(command);
updateScreenNames(command);
public void applyCommands(Command[] commands) {
super.applyCommands(commands);
getSupportFragmentManager().executePendingTransactions();
printScreensScheme();
}
};
@@ -73,16 +52,15 @@ 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(new Screens.SampleScreen(1))});
} else {
screenNames = (List<String>) savedInstanceState.getSerializable(STATE_SCREEN_NAMES);
printScreensScheme();
}
}
@Override
protected void onResume() {
super.onResume();
protected void onResumeFragments() {
super.onResumeFragments();
navigatorHolder.setNavigator(navigator);
}
@@ -104,41 +82,27 @@ public class MainActivity extends MvpAppCompatActivity {
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
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<SampleFragment> fragments = new ArrayList<>();
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment instanceof SampleFragment) {
fragments.add((SampleFragment) fragment);
}
}
screensSchemeTV.setText("Chain: " + str + "");
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() + "");
}
}
@@ -27,6 +27,7 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
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;
@@ -34,9 +35,9 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
private View replaceCommandBt;
private View newChainCommandBt;
private View newRootCommandBt;
private View backWithMessageCommandBt;
private View forwardWithDelayCommandBt;
private View backToCommandBt;
private View finishChainCommandBt;
@Inject
Router router;
@@ -54,11 +55,20 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
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);
@@ -80,9 +90,9 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
replaceCommandBt = view.findViewById(R.id.replace_command);
newChainCommandBt = view.findViewById(R.id.new_chain_command);
newRootCommandBt = view.findViewById(R.id.new_root_command);
backWithMessageCommandBt = view.findViewById(R.id.back_with_message_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
@@ -124,12 +134,6 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
presenter.onNewRootCommandClick();
}
});
backWithMessageCommandBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onBackWithMessageCommandClick();
}
});
forwardWithDelayCommandBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@@ -142,6 +146,12 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
presenter.onBackToCommandClick();
}
});
finishChainCommandBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onFinishChainCommandClick();
}
});
}
@Override
@@ -1,10 +1,7 @@
package ru.terrakok.cicerone.sample.ui.start;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.arellomobile.mvp.MvpAppCompatActivity;
import com.arellomobile.mvp.presenter.InjectPresenter;
@@ -15,18 +12,11 @@ import javax.inject.Inject;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.commands.SystemMessage;
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.mvp.start.StartActivityPresenter;
import ru.terrakok.cicerone.sample.mvp.start.StartActivityView;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
/**
* Created by terrakok 21.11.16
@@ -41,6 +31,8 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
@InjectPresenter
StartActivityPresenter presenter;
private Navigator navigator = new SupportAppNavigator(this, -1);
@ProvidePresenter
public StartActivityPresenter createStartActivityPresenter() {
return new StartActivityPresenter(router);
@@ -68,6 +60,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
@@ -86,56 +84,4 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
public void onBackPressed() {
presenter.onBackPressed();
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof SystemMessage) {
Toast.makeText(StartActivity.this, ((SystemMessage) command).getMessage(), Toast.LENGTH_SHORT).show();
} else {
Log.e("Cicerone", "Illegal command for this screen: " + command.getClass().getSimpleName());
}
}
private void forward(Forward command) {
switch (command.getScreenKey()) {
case Screens.START_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, StartActivity.class));
break;
case Screens.MAIN_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, MainActivity.class));
break;
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, BottomNavigationActivity.class));
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
}
}
private void replace(Replace command) {
switch (command.getScreenKey()) {
case Screens.START_ACTIVITY_SCREEN:
case Screens.MAIN_ACTIVITY_SCREEN:
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
forward(new Forward(command.getScreenKey(), command.getTransitionData()));
finish();
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
}
}
private void back() {
finish();
}
};
}
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>
+43 -27
View File
@@ -13,26 +13,30 @@
app:navigationIcon="@drawable/ic_arrow_back_white_24dp"
app:title="@string/app_name"/>
<LinearLayout
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:columnCount="2">
<TextView
android:layout_margin="4dp"
android:id="@+id/back_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/carrot"
android:gravity="center"
android:text="Back"
android:layout_columnWeight="1"
android:textSize="28sp"/>
<TextView
android:id="@+id/forward_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/sunflower"
android:gravity="center"
android:text="Forward"
@@ -40,9 +44,11 @@
<TextView
android:id="@+id/replace_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/greensea"
android:gravity="center"
android:text="Replace"
@@ -50,9 +56,11 @@
<TextView
android:id="@+id/new_chain_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/nephritis"
android:gravity="center"
android:text="New chain"
@@ -60,29 +68,23 @@
<TextView
android:id="@+id/new_root_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/belizehole"
android:gravity="center"
android:text="New root"
android:textSize="28sp"/>
<TextView
android:id="@+id/back_with_message_command"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/alizarin"
android:gravity="center"
android:text="Back with message"
android:textSize="28sp"/>
<TextView
android:id="@+id/forward_delay_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/carrot"
android:gravity="center"
android:text="Forward with 5 sec delay"
@@ -90,13 +92,27 @@
<TextView
android:id="@+id/back_to_command"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_rowWeight="1"
android:background="@color/sunflower"
android:gravity="center"
android:text="Back to '3'"
android:textSize="28sp"/>
</LinearLayout>
<TextView
android:id="@+id/finish_chain_command"
android:layout_width="0dp"
android:layout_columnWeight="1"
android:layout_margin="4dp"
android:layout_height="0dp"
android:layout_rowWeight="1"
android:background="@color/greensea"
android:gravity="center"
android:text="Finish chain"
android:textSize="28sp"/>
</GridLayout>
</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>