62 Commits

Author SHA1 Message Date
Konstantin Tskhovrebov dcf1532103 Merge branch 'develop' 2020-02-11 14:42:17 +03:00
Konstantin Tskhovrebov 046410e132 Update version of Cicerone to "5.1.0". 2020-02-11 14:41:24 +03:00
Konstantin e80b2d5432 Merge pull request #111 from terrakok/handle_command_error
Add errorOnApplyCommand for ability manual error handling.
2020-02-11 14:38:09 +03:00
Konstantin Tskhovrebov 3f1cf714df Add errorOnApplyCommand for ability manual error handling. 2020-02-11 14:36:13 +03:00
Konstantin febae878a4 Merge pull request #99 from asitnkova/feature/add_nullability_annotations
Add androidx nullability annotations
2020-02-11 12:45:10 +03:00
Konstantin 6cc2d3e474 Merge branch 'develop' into feature/add_nullability_annotations 2020-02-11 12:42:04 +03:00
Konstantin dc2f732ec6 Merge pull request #110 from asitnkova/feature/add_new_methods_fragment_1.2.0
Feature/add new methods fragment 1.2.0
2020-02-11 12:38:47 +03:00
asitnkova 5d6880c95f Add support for new FragmentTransaction.replace overload 2020-02-04 22:34:10 +07:00
Alexander e1e2dd8b9b Bump build tools 3.5.0-alpha01 -> 3.5.0, gradle 5.1-milestone-1 -> 5.6.4 2020-02-04 22:32:51 +07:00
Konstantin 4df0f34e34 Merge pull request #101 from Anton111111/develop
Made properties protected to add ability use them in overridden methods.
2019-11-18 12:52:07 +03:00
anton f36f6a1e44 Made properties protected to add ability use them in overridden methods. 2019-09-17 19:42:11 +03:00
Anton Potekhin be63c91abe Made properties protected to add ability use them in overridden methods. 2019-02-07 09:36:05 +03:00
Alexander Sitnikov 8e9bf90ee6 Add nullability annotations 2019-01-26 17:31:29 +07:00
terrakok 0ba13efe29 Merge branch 'develop' 2019-01-25 22:13:32 +03:00
terrakok d5737bb092 Update version of Cicerone to "5.0.0". 2019-01-25 22:13:14 +03:00
terrakok 07869ad6e7 Update gradle plugin. 2019-01-25 22:09:19 +03:00
Konstantin a57b8ecb3c Merge pull request #91 from pihariev/fix/sample_chain_representation
Fix sample chain representation for ordinary navigation.
2018-11-05 22:58:21 +03:00
Konstantin 8e3f41057d Merge pull request #88 from pihariev/refactor/androidx
Refactor library and sample to AndroidX packaging system.
2018-11-05 22:12:00 +03:00
Roman Pihariev 7823dd0658 Fix sample chain representation for ordinary navigation. 2018-11-05 16:10:43 +02:00
Roman Pihariev 6b195590de Refactor library and sample to AndroidX packaging system. 2018-11-02 22:44:01 +02:00
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
78 changed files with 1330 additions and 1802 deletions
+22 -45
View File
@@ -22,8 +22,11 @@ It was designed to be used with the MVP pattern (try [Moxy](https://github.com/A
+ suitable for Unit Testing
## Additional features
+ easy screen result subscription
+ 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
@@ -64,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.
@@ -81,14 +84,14 @@ 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:
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.
@@ -122,8 +125,8 @@ 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)
}
};
```
@@ -131,53 +134,27 @@ private Navigator navigator = new Navigator() {
## Navigation commands
This commands set will fulfill the needs of the most applications. But if you need something special - just add it!
+ Forward - Opens new screen
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
![](https://github.com/terrakok/Cicerone/raw/develop/media/forward_img.png)
+ Back - Rolls back the last transition
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
![](https://github.com/terrakok/Cicerone/raw/develop/media/back_img.png)
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
![](https://github.com/terrakok/Cicerone/raw/develop/media/backTo_img.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)
![](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 SupportAppNavigator(this, R.id.container) {
@Override
protected Intent createActivityIntent(Context context, String screenKey, Object data) {
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
switch (screenKey) {
case Screens.PROFILE_SCREEN:
return new ProfileFragment();
case Screens.SELECT_PHOTO_SCREEN:
return SelectPhotoFragment.getNewInstance((int) data);
}
return null;
}
@Override
protected void setupFragmentTransactionAnimation(
Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
//setup animation
}
};
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/web/a94/d73/653/a94d736534694d9daa994e0c260fca28.gif)
![](https://habrastorage.org/web/6dd/a19/15c/6dda1915cdcf4f14bed16fcffb3fd938.gif)
![](https://habrastorage.org/web/a63/881/7f8/a638817f8bba49daacc4fa427987fabb.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)
@@ -207,4 +184,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
```
-178
View File
@@ -1,178 +0,0 @@
# Cicerone
[![Telegram](https://img.shields.io/badge/Telegram-RUS-blue.svg)](https://t.me/Cicerone_RUS)
![](https://habrastorage.org/files/644/32e/9eb/64432e9eb3664723b3ee438449dab3b0.png)
Cicerone (_"чи-че-ро́-не"_ - устар. гид) - легкая библиотека для простой реализации навигации в андроид приложении.
Разработана для использования в MVP архитектуре (попробуйте [Moxy](https://github.com/Arello-Mobile/Moxy)), но легко встраивается в любые решения.
## Основные преимущества
+ не завязана на Fragment'ы
+ не фреймворк
+ короткие вызовы навигации (никаких билдеров)
+ lifecycle-безопасна!
+ простое расширение функционала
+ приспособлена для Unit тестов
## Как подключить?
Добавьте в build.gradle зависимость:
```groovy
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:X.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_
**Внимание**: В FragmentActivity следует использовать _onResumeFragments()_. ([подробнее](https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume()))
```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:3.0.1'
classpath 'com.android.tools.build:gradle:3.5.0'
// 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 -1
View File
@@ -12,7 +12,8 @@
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
android.enableJetifier=true
android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+2 -2
View File
@@ -1,6 +1,6 @@
#Sat Dec 23 00:31:04 MSK 2017
#Fri Jan 25 22:04:15 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+4 -2
View File
@@ -7,6 +7,8 @@ targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly project(':stub-android')
compileOnly 'com.google.android:android:4.1.1.4'
implementation "org.jetbrains:annotations:16.0.3"
}
ext {
@@ -15,7 +17,7 @@ ext {
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
artifact = 'cicerone'
libraryVersion = '3.0.0'
libraryVersion = '5.1.0'
gitUrl = 'https://github.com/terrakok/Cicerone'
allLicenses = ['MIT']
}
@@ -23,7 +25,7 @@ ext {
// Configuration of the library uploading to the Bintray
// Note: Call 'bintrayUpload' task (it will execute 'install' task first)
// I try to include as little properties as possible in these files
project.archivesBaseName ='cicerone' // to fix that project name different from artifact name
project.archivesBaseName = 'cicerone' // to fix that project name different from artifact name
apply from: 'androidmaven.gradle'
apply from: 'bintray.gradle'
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
import ru.terrakok.cicerone.commands.Command;
/**
@@ -17,6 +19,7 @@ public abstract class BaseRouter {
this.commandBuffer = new CommandBuffer();
}
@NotNull
CommandBuffer getCommandBuffer() {
return commandBuffer;
}
@@ -26,7 +29,7 @@ public abstract class BaseRouter {
*
* @param commands navigation command array to execute
*/
protected void executeCommands(Command... commands) {
protected void executeCommands(@NotNull Command... commands) {
commandBuffer.executeCommands(commands);
}
}
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
/**
* Cicerone is the holder for other library components.
* To use it, instantiate it using one of the {@link #create()} methods.
@@ -15,14 +17,16 @@ package ru.terrakok.cicerone;
public class Cicerone<T extends BaseRouter> {
private T router;
private Cicerone(T router) {
private Cicerone(@NotNull T router) {
this.router = router;
}
@NotNull
public NavigatorHolder getNavigatorHolder() {
return router.getCommandBuffer();
}
@NotNull
public T getRouter() {
return router;
}
@@ -30,6 +34,7 @@ public class Cicerone<T extends BaseRouter> {
/**
* Creates the Cicerone instance with the default {@link Router router}
*/
@NotNull
public static Cicerone<Router> create() {
return create(new Router());
}
@@ -38,7 +43,8 @@ public class Cicerone<T extends BaseRouter> {
* Creates the Cicerone instance with the custom router.
* @param customRouter the custom router extending {@link BaseRouter}
*/
public static <T extends BaseRouter> Cicerone<T> create(T customRouter) {
@NotNull
public static <T extends BaseRouter> Cicerone<T> create(@NotNull T customRouter) {
return new Cicerone<>(customRouter);
}
}
@@ -4,6 +4,9 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedList;
import java.util.Queue;
@@ -18,7 +21,7 @@ class CommandBuffer implements NavigatorHolder {
private Queue<Command[]> pendingCommands = new LinkedList<>();
@Override
public void setNavigator(Navigator navigator) {
public void setNavigator(@Nullable Navigator navigator) {
this.navigator = navigator;
while (!pendingCommands.isEmpty()) {
if (navigator != null) {
@@ -37,7 +40,7 @@ class CommandBuffer implements NavigatorHolder {
* Else puts it to the pending commands queue to pass it later.
* @param commands navigation command array
*/
void executeCommands(Command[] commands) {
void executeCommands(@NotNull Command[] commands) {
if (navigator != null) {
navigator.applyCommands(commands);
} else {
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
import ru.terrakok.cicerone.commands.Command;
/**
@@ -17,5 +19,5 @@ public interface Navigator {
*
* @param commands the navigation command array to apply per single transaction
*/
void applyCommands(Command[] commands);
void applyCommands(@NotNull Command[] commands);
}
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
/**
* Navigator holder interface.
* Use it to connect a {@link Navigator} to the {@link Cicerone}.
@@ -15,7 +17,7 @@ public interface NavigatorHolder {
*
* @param navigator new active Navigator
*/
void setNavigator(Navigator navigator);
void setNavigator(@NotNull Navigator navigator);
/**
* Remove the current Navigator and stop receive commands.
@@ -4,14 +4,14 @@
package ru.terrakok.cicerone;
import java.util.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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;
import ru.terrakok.cicerone.result.ResultListener;
/**
* Router is the class for high-level navigation.
@@ -21,148 +21,80 @@ import ru.terrakok.cicerone.result.ResultListener;
*/
public class Router extends BaseRouter {
private HashMap<Integer, ResultListener> resultListeners = new HashMap<>();
public Router() {
super();
}
/**
* Subscribe to the screen result.<br>
* <b>Note:</b> only one listener can subscribe to a unique resultCode!<br>
* You must call a <b>removeResultListener()</b> to avoid a memory leak.
*
* @param resultCode key for filter results
* @param listener result listener
*/
public void setResultListener(Integer resultCode, ResultListener listener) {
resultListeners.put(resultCode, listener);
}
/**
* Unsubscribe from the screen result.
*
* @param resultCode key for filter results
*/
public void removeResultListener(Integer resultCode) {
resultListeners.remove(resultCode);
}
/**
* Send result data to subscriber.
*
* @param resultCode result data key
* @param result result data
* @return TRUE if listener was notified and FALSE otherwise
*/
protected boolean sendResult(Integer resultCode, Object result) {
ResultListener resultListener = resultListeners.get(resultCode);
if (resultListener != null) {
resultListener.onResult(result);
return true;
}
return false;
}
/**
* Open new screen and add it to the screens chain.
*
* @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) {
executeCommands(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) {
executeCommands(
new BackTo(null),
new Forward(screenKey, data)
);
public void navigateTo(@NotNull 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) {
public void newRootScreen(@NotNull Screen screen) {
executeCommands(
new BackTo(null),
new Replace(screenKey, data)
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(@NotNull 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) {
executeCommands(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) {
executeCommands(new BackTo(screenKey));
public void backTo(@Nullable Screen screen) {
executeCommands(new BackTo(screen));
}
/**
* Opens several screens inside single transaction.
* @param screens
*/
public void newChain(@NotNull 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(@NotNull 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);
}
/**
@@ -185,35 +117,4 @@ public class Router extends BaseRouter {
executeCommands(new Back());
}
/**
* Return to the previous screen in the chain and send result data.
*
* @param resultCode result data key
* @param result result data
*/
public void exitWithResult(Integer resultCode, Object result) {
exit();
sendResult(resultCode, result);
}
/**
* Return to the previous screen in the chain and show system message.
*
* @param message message to show
*/
public void exitWithMessage(String message) {
executeCommands(
new Back(),
new SystemMessage(message)
);
}
/**
* Show system message.
*
* @param message message to show
*/
public void showSystemMessage(String message) {
executeCommands(new SystemMessage(message));
}
}
@@ -0,0 +1,15 @@
package ru.terrakok.cicerone;
import org.jetbrains.annotations.NotNull;
/**
* Screen is class for description application screen.
*/
public abstract class Screen {
protected String screenKey = getClass().getCanonicalName();
@NotNull
public String getScreenKey() {
return screenKey;
}
}
@@ -1,122 +0,0 @@
/*
* Created by Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
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>
*/
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 this method to create option for start activity
*
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
* @param activityIntent activity intent
* @return transition options
*/
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
return null;
}
@Override
protected void forward(Forward command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Start activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
} else {
super.forward(command);
}
}
@Override
protected void replace(Replace command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Replace activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
activity.finish();
} else {
super.replace(command);
}
}
private void checkAndStartActivity(String screenKey, Intent activityIntent, Bundle options) {
// Check if we can start activity
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(activityIntent, options);
} else {
unexistingActivity(screenKey, activityIntent);
}
}
/**
* Called when there is no activity to open {@code screenKey}.
*
* @param screenKey screen key
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(String screenKey, Intent activityIntent) {
// Do nothing by default
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param context
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(Context context, 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,254 +0,0 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.android;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import java.util.LinkedList;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
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;
/**
* {@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(String)} 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;
private LinkedList<String> localStackCopy;
/**
* 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 this method to setup custom fragment transaction animation.
*
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
* @param currentFragment current fragment in container
* (for {@link Replace} command it will be screen previous in new chain, NOT replaced screen)
* @param nextFragment next screen fragment
* @param fragmentTransaction fragment transaction
*/
protected void setupFragmentTransactionAnimation(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
}
@Override
public void applyCommands(Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
applyCommand(command);
}
}
private void copyStackToLocal() {
localStackCopy = new LinkedList<>();
final int stackSize = fragmentManager.getBackStackEntryCount();
for (int i = 0; i < stackSize; i++) {
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).getName());
}
}
/**
* Perform transition described by the navigation command
*
* @param command the navigation command to apply
*/
protected void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof BackTo) {
backTo((BackTo) command);
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
/**
* Performs {@link Forward} command transition
*/
protected void forward(Forward command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
}
/**
* Performs {@link Forward} command transition
*/
protected void back() {
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
} else {
exit();
}
}
/**
* Performs {@link Replace} command transition
*/
protected void replace(Replace command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.commit();
}
}
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
String key = command.getScreenKey();
if (key == null) {
backToRoot();
} else {
int index = localStackCopy.indexOf(key);
int size = localStackCopy.size();
if (index != -1) {
for (int i = 1; i < size - index; i++) {
localStackCopy.pop();
}
fragmentManager.popBackStack(key, 0);
} else {
backToUnexisting(command.getScreenKey());
}
}
}
private void backToRoot() {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
localStackCopy.clear();
}
/**
* 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 (via {@link BackTo} command),
* but didn't found it.
* @param screenKey screen key
*/
protected void backToUnexisting(String screenKey) {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
@@ -1,122 +0,0 @@
/*
* Created by Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
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>
*/
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 this method to create option for start activity
*
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
* @param activityIntent activity intent
* @return transition options
*/
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
return null;
}
@Override
protected void forward(Forward command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Start activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
} else {
super.forward(command);
}
}
@Override
protected void replace(Replace command) {
Intent activityIntent = createActivityIntent(activity, command.getScreenKey(), command.getTransitionData());
// Replace activity
if (activityIntent != null) {
Bundle options = createStartActivityOptions(command, activityIntent);
checkAndStartActivity(command.getScreenKey(), activityIntent, options);
activity.finish();
} else {
super.replace(command);
}
}
private void checkAndStartActivity(String screenKey, Intent activityIntent, Bundle options) {
// Check if we can start activity
if (activityIntent.resolveActivity(activity.getPackageManager()) != null) {
activity.startActivity(activityIntent, options);
} else {
unexistingActivity(screenKey, activityIntent);
}
}
/**
* Called when there is no activity to open {@code screenKey}.
*
* @param screenKey screen key
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(String screenKey, Intent activityIntent) {
// Do nothing by default
}
/**
* 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(Context context, 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,253 +0,0 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.android;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import java.util.LinkedList;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
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;
/**
* {@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(String)} 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;
protected LinkedList<String> localStackCopy;
/**
* 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 this method to setup custom fragment transaction animation.
*
* @param command current navigation command. Will be only {@link Forward} or {@link Replace}
* @param currentFragment current fragment in container
* (for {@link Replace} command it will be screen previous in new chain, NOT replaced screen)
* @param nextFragment next screen fragment
* @param fragmentTransaction fragment transaction
*/
protected void setupFragmentTransactionAnimation(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
}
@Override
public void applyCommands(Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
applyCommand(command);
}
}
private void copyStackToLocal() {
localStackCopy = new LinkedList<>();
final int stackSize = fragmentManager.getBackStackEntryCount();
for (int i = 0; i < stackSize; i++) {
localStackCopy.add(fragmentManager.getBackStackEntryAt(i).getName());
}
}
/**
* Perform transition described by the navigation command
*
* @param command the navigation command to apply
*/
protected void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof BackTo) {
backTo((BackTo) command);
} else if (command instanceof SystemMessage) {
showSystemMessage(((SystemMessage) command).getMessage());
}
}
/**
* Performs {@link Forward} command transition
*/
protected void forward(Forward command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
}
/**
* Performs {@link Back} command transition
*/
protected void back() {
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
} else {
exit();
}
}
/**
* Performs {@link Replace} command transition
*/
protected void replace(Replace command) {
Fragment fragment = createFragment(command.getScreenKey(), command.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.pop();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(command.getScreenKey())
.commit();
localStackCopy.add(command.getScreenKey());
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransactionAnimation(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.commit();
}
}
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
String key = command.getScreenKey();
if (key == null) {
backToRoot();
} else {
int index = localStackCopy.indexOf(key);
int size = localStackCopy.size();
if (index != -1) {
for (int i = 1; i < size - index; i++) {
localStackCopy.pop();
}
fragmentManager.popBackStack(key, 0);
} else {
backToUnexisting(command.getScreenKey());
}
}
}
private void backToRoot() {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
localStackCopy.clear();
}
/**
* 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 (via {@link BackTo} command),
* but didn't found it.
* @param screenKey screen key
*/
protected void backToUnexisting(String screenKey) {
backToRoot();
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
throw new RuntimeException("Can't create a screen for passed screenKey.");
}
}
@@ -0,0 +1,304 @@
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.*;
import java.util.LinkedList;
/**
* 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 {
protected final Activity activity;
protected final FragmentManager fragmentManager;
protected final int containerId;
protected LinkedList<String> localStackCopy;
public AppNavigator(@NotNull Activity activity, int containerId) {
this(activity, activity.getFragmentManager(), containerId);
}
public AppNavigator(@NotNull Activity activity, @NotNull FragmentManager fragmentManager, int containerId) {
this.activity = activity;
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommands(@NotNull Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
try {
applyCommand(command);
} catch (RuntimeException e) {
errorOnApplyCommand(command, e);
}
}
}
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(@NotNull 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(@NotNull 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(@NotNull 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(@NotNull 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(@NotNull 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(@NotNull 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(@NotNull Command command,
@Nullable Fragment currentFragment,
@Nullable Fragment nextFragment,
@NotNull 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
*/
@Nullable
protected Bundle createStartActivityOptions(@NotNull Command command, @NotNull Intent activityIntent) {
return null;
}
private void checkAndStartActivity(@NotNull AppScreen screen,
@NotNull Intent activityIntent,
@Nullable 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(@NotNull AppScreen screen, @NotNull Intent activityIntent) {
// Do nothing by default
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screen screen
* @return instantiated fragment for the passed screen
*/
@Nullable
protected Fragment createFragment(@NotNull AppScreen screen) {
Fragment fragment = screen.getFragment();
if (fragment == null) {
errorWhileCreatingScreen(screen);
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
}
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(@NotNull AppScreen screen) {
backToRoot();
}
/**
* Called when we tried to create new intent or fragment but didn't receive them.
*
* @param screen screen
*/
protected void errorWhileCreatingScreen(@NotNull AppScreen screen) {
// Do nothing by default
}
/**
* Override this method if you want to handle apply command error.
*
* @param command command
* @param error error
*/
protected void errorOnApplyCommand(
@NotNull Command command,
@NotNull RuntimeException error
) {
throw error;
}
}
@@ -0,0 +1,27 @@
package ru.terrakok.cicerone.android.pure;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import org.jetbrains.annotations.Nullable;
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 {
@Nullable
public Fragment getFragment() {
return null;
}
@Nullable
public Intent getActivityIntent(Context context) {
return null;
}
}
@@ -0,0 +1,36 @@
package ru.terrakok.cicerone.android.support;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class FragmentParams {
@NotNull
private final Class<? extends Fragment> fragmentClass;
@Nullable
private final Bundle arguments;
@NotNull
public final Class<? extends Fragment> getFragmentClass() {
return this.fragmentClass;
}
@Nullable
public final Bundle getArguments() {
return this.arguments;
}
public FragmentParams(@NotNull Class<? extends Fragment> fragmentClass, @Nullable Bundle arguments) {
this.fragmentClass = fragmentClass;
this.arguments = arguments;
}
public FragmentParams(@NotNull Class<? extends Fragment> fragmentClass) {
this.fragmentClass = fragmentClass;
this.arguments = null;
}
}
@@ -0,0 +1,318 @@
package ru.terrakok.cicerone.android.support;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.*;
import java.util.LinkedList;
/**
* 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 {
protected final Activity activity;
protected final FragmentManager fragmentManager;
protected final int containerId;
protected LinkedList<String> localStackCopy;
public SupportAppNavigator(@NotNull FragmentActivity activity, int containerId) {
this(activity, activity.getSupportFragmentManager(), containerId);
}
public SupportAppNavigator(@NotNull FragmentActivity activity, @NotNull FragmentManager fragmentManager, int containerId) {
this.activity = activity;
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommands(@NotNull Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
try {
applyCommand(command);
} catch (RuntimeException e) {
errorOnApplyCommand(command, e);
}
}
}
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(@NotNull 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(@NotNull 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(@NotNull Forward command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
FragmentParams fragmentParams = screen.getFragmentParams();
Fragment fragment = fragmentParams == null ? createFragment(screen) : null;
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransaction(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction);
if (fragmentParams != null) {
fragmentTransaction.replace(containerId, fragmentParams.getFragmentClass(), fragmentParams.getArguments());
} else {
fragmentTransaction.replace(containerId, fragment);
}
fragmentTransaction
.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(@NotNull 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(@NotNull Replace command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
FragmentParams fragmentParams = screen.getFragmentParams();
Fragment fragment = fragmentParams == null ? createFragment(screen) : null;
if (localStackCopy.size() > 0) {
fragmentManager.popBackStack();
localStackCopy.removeLast();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransaction(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
if (fragmentParams != null) {
fragmentTransaction.replace(
containerId,
fragmentParams.getFragmentClass(),
fragmentParams.getArguments());
} else {
fragmentTransaction.replace(containerId, fragment);
}
fragmentTransaction
.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(@NotNull 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(@NotNull Command command,
@Nullable Fragment currentFragment,
@Nullable Fragment nextFragment,
@NotNull 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
*/
@Nullable
protected Bundle createStartActivityOptions(@NotNull Command command, @NotNull Intent activityIntent) {
return null;
}
private void checkAndStartActivity(@NotNull SupportAppScreen screen, @NotNull Intent activityIntent, @Nullable 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(@NotNull SupportAppScreen screen, @NotNull Intent activityIntent) {
// Do nothing by default
}
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screen screen
* @return instantiated fragment for the passed screen
*/
@Nullable
protected Fragment createFragment(@NotNull SupportAppScreen screen) {
Fragment fragment = screen.getFragment();
if (fragment == null) {
errorWhileCreatingScreen(screen);
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
}
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(@NotNull SupportAppScreen screen) {
backToRoot();
}
/**
* Called when we tried to create new intent or fragment but didn't receive them.
*
* @param screen screen
*/
protected void errorWhileCreatingScreen(@NotNull SupportAppScreen screen) {
// Do nothing by default
}
/**
* Override this method if you want to handle apply command error.
*
* @param command command
* @param error error
*/
protected void errorOnApplyCommand(
@NotNull Command command,
@NotNull RuntimeException error
) {
throw error;
}
}
@@ -0,0 +1,33 @@
package ru.terrakok.cicerone.android.support;
import android.content.Context;
import android.content.Intent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import androidx.fragment.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 {
@Nullable
public Fragment getFragment() {
return null;
}
@Nullable
public Intent getActivityIntent(@NotNull Context context) {
return null;
}
@Nullable
public FragmentParams getFragmentParams() {
return null;
}
}
@@ -5,7 +5,7 @@
package ru.terrakok.cicerone.commands;
/**
* Rolls back the last transition from the screens chain.
* Rolls fragmentBack the last transition from the screens chain.
*/
public class Back implements Command {
@@ -4,26 +4,30 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.Nullable;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.Screen;
/**
* 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(@Nullable Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
@Nullable
public Screen getScreen() {
return screen;
}
}
@@ -4,29 +4,27 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.NotNull;
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(@NotNull Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
@NotNull
public Screen getScreen() {
return screen;
}
}
@@ -4,29 +4,28 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.NotNull;
import ru.terrakok.cicerone.Screen;
/**
* Replaces the current screen.
*/
public class Replace implements Command {
private String screenKey;
private Object transitionData;
@NotNull
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(@NotNull Screen screen) {
this.screen = screen;
}
public String getScreenKey() {
return screenKey;
}
public Object getTransitionData() {
return transitionData;
@NotNull
public Screen getScreen() {
return screen;
}
}
@@ -1,25 +0,0 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.commands;
/**
* 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;
}
}
@@ -1,15 +0,0 @@
/*
* Created by Konstantin Tskhovrebov (aka @terrakok)
*/
package ru.terrakok.cicerone.result;
public interface ResultListener {
/**
* Received result from screen.
*
* @param resultData
*/
void onResult(Object resultData);
}
+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,26 +0,0 @@
package android.app;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
/***
* Created by laomo on 2016-11-21.
*/
public class Activity extends Context {
public void startActivity(Intent intent) {
throw new RuntimeException("Stub!");
}
public void startActivity(Intent intent, Bundle options) {
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,42 +0,0 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
public class FragmentManager {
public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
}
public boolean executePendingTransactions() {
throw new RuntimeException("Stub!");
}
public void popBackStack() {
throw new RuntimeException("Stub!");
}
public void popBackStack(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 Fragment findFragmentById(int id) {
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,5 +0,0 @@
package android.content;
public class ComponentName {
}
@@ -1,11 +0,0 @@
package android.content;
import android.content.pm.PackageManager;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Context {
public PackageManager getPackageManager() { throw new RuntimeException("Stub!"); }
}
@@ -1,12 +0,0 @@
package android.content;
import android.content.pm.PackageManager;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 22.02.17
*/
public class Intent {
public ComponentName resolveActivity(PackageManager pm) { throw new RuntimeException("Stub!"); }
}
@@ -1,5 +0,0 @@
package android.content.pm;
public class PackageManager {
}
@@ -1,7 +0,0 @@
package android.os;
/***
* Created by laomo on 2016-11-21.
*/
public class Bundle {
}
@@ -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!");
}
}
@@ -1,6 +1,7 @@
package android.support.v4.app;
package androidx.fragment.app;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
@@ -1,4 +1,4 @@
package android.support.v4.app;
package androidx.fragment.app;
import android.app.Activity;
@@ -1,4 +1,4 @@
package android.support.v4.app;
package androidx.fragment.app;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
@@ -1,4 +1,6 @@
package android.support.v4.app;
package androidx.fragment.app;
import android.os.Bundle;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
@@ -10,6 +12,13 @@ public class FragmentTransaction {
throw new RuntimeException("Stub!");
}
public final FragmentTransaction replace(
int containerViewId,
Class<? extends Fragment> fragmentClass,
Bundle args) {
throw new RuntimeException("Stub!");
}
public FragmentTransaction addToBackStack(String name) {
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

+7 -8
View File
@@ -5,12 +5,11 @@ repositories {
}
android {
compileSdkVersion 25
buildToolsVersion '26.0.2'
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 25
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0.0"
applicationId "ru.terrakok.cicerone.sample"
@@ -28,15 +27,15 @@ android {
}
ext {
supportLibraryVersion = "25.3.0"
androidXVersion = "1.0.0"
moxyVersion = "1.4.6"
daggerVersion = "2.10"
}
dependencies {
// Support libraries
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:design:$supportLibraryVersion"
implementation "androidx.appcompat:appcompat:$androidXVersion"
implementation "com.google.android.material:material:$androidXVersion"
//MVP Moxy
implementation "com.arello-mobile:moxy:$moxyVersion"
@@ -44,7 +43,7 @@ dependencies {
annotationProcessor "com.arello-mobile:moxy-compiler:$moxyVersion"
//Cicerone
compile project(':library')
implementation project(':library')
//DI
implementation "com.google.dagger:dagger:$daggerVersion"
@@ -1,24 +1,115 @@
package ru.terrakok.cicerone.sample;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.fragment.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 String PROFILE_SCREEN = "profile screen";
public static final String SELECT_PHOTO_SCREEN = "select photo screen";
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,7 @@ 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;
@@ -21,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 {
@@ -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);
}
}
@@ -5,6 +5,7 @@ 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.
@@ -13,11 +14,11 @@ import ru.terrakok.cicerone.sample.R;
@InjectViewState
public class SelectPhotoPresenter extends MvpPresenter<SelectPhotoView> {
private Router router;
private final int RESULT_CODE;
private PhotoSelection photoSelection;
public SelectPhotoPresenter(Router router, int resultCode) {
public SelectPhotoPresenter(PhotoSelection photoSelection, Router router) {
this.photoSelection = photoSelection;
this.router = router;
RESULT_CODE = resultCode;
}
@Override
@@ -33,7 +34,8 @@ public class SelectPhotoPresenter extends MvpPresenter<SelectPhotoView> {
}
public void onPhotoClick(int photoRes) {
router.exitWithResult(RESULT_CODE, photoRes);
photoSelection.setSelectedPhoto(photoRes);
router.exit();
}
public void onBackPressed() {
@@ -4,8 +4,8 @@ import com.arellomobile.mvp.InjectViewState;
import com.arellomobile.mvp.MvpPresenter;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.result.ResultListener;
import ru.terrakok.cicerone.sample.Screens;
import ru.terrakok.cicerone.sample.mvp.animation.PhotoSelection;
/**
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
@@ -13,19 +13,17 @@ import ru.terrakok.cicerone.sample.Screens;
@InjectViewState
public class ProfilePresenter extends MvpPresenter<ProfileView> {
private static final int PHOTO_RESULT_CODE = 42;
private Router router;
private int currentPhoto;
private PhotoSelection photoSelection;
public ProfilePresenter(int defaultPhoto, Router router) {
currentPhoto = defaultPhoto;
public ProfilePresenter(PhotoSelection photoSelection, Router router) {
this.photoSelection = photoSelection;
this.router = router;
router.setResultListener(PHOTO_RESULT_CODE, new ResultListener() {
photoSelection.setListener(new PhotoSelection.Listener() {
@Override
public void onResult(Object resultData) {
currentPhoto = (int) resultData;
public void onChange(int selectedPhoto) {
updatePhoto();
}
});
@@ -39,16 +37,16 @@ public class ProfilePresenter extends MvpPresenter<ProfileView> {
@Override
public void onDestroy() {
router.removeResultListener(PHOTO_RESULT_CODE);
photoSelection.setListener(null);
super.onDestroy();
}
private void updatePhoto() {
getViewState().showPhoto(currentPhoto);
getViewState().showPhoto(photoSelection.getSelectedPhoto());
}
public void onPhotoClicked() {
router.navigateTo(Screens.SELECT_PHOTO_SCREEN, PHOTO_RESULT_CODE);
router.navigateTo(new Screens.SelectPhotoScreen());
}
public void onBackPressed() {
@@ -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,13 +67,20 @@ public class SamplePresenter extends MvpPresenter<SampleView> {
future = executorService.schedule(new Runnable() {
@Override
public void run() {
//WARNING! Navigation must be only in UI thread. this method works only for sample :)
router.navigateTo(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
//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,15 +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(Screens.PROFILE_SCREEN);
router.navigateTo(new Screens.ProfileScreen());
}
public void onBackPressed() {
@@ -1,12 +1,10 @@
package ru.terrakok.cicerone.sample.ui.animations;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import android.transition.ChangeBounds;
import android.view.View;
@@ -14,7 +12,7 @@ import javax.inject.Inject;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.android.SupportAppNavigator;
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;
@@ -43,7 +41,7 @@ public class ProfileActivity extends AppCompatActivity {
setContentView(R.layout.activity_container);
if (savedInstanceState == null) {
navigator.applyCommands(new Command[]{new Replace(Screens.PROFILE_SCREEN, null)});
navigator.applyCommands(new Command[]{new Replace(new Screens.ProfileInfoScreen())});
}
}
@@ -61,23 +59,7 @@ public class ProfileActivity extends AppCompatActivity {
private Navigator navigator = new SupportAppNavigator(this, R.id.container) {
@Override
protected Intent createActivityIntent(Context context, String screenKey, Object data) {
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
switch (screenKey) {
case Screens.PROFILE_SCREEN:
return new ProfileFragment();
case Screens.SELECT_PHOTO_SCREEN:
return SelectPhotoFragment.getNewInstance((int) data);
}
return null;
}
@Override
protected void setupFragmentTransactionAnimation(Command command, Fragment currentFragment, Fragment nextFragment, FragmentTransaction fragmentTransaction) {
protected void setupFragmentTransaction(Command command, Fragment currentFragment, Fragment nextFragment, FragmentTransaction fragmentTransaction) {
if (command instanceof Forward
&& currentFragment instanceof ProfileFragment
&& nextFragment instanceof SelectPhotoFragment) {
@@ -1,12 +1,12 @@
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 androidx.annotation.Nullable;
import com.arellomobile.mvp.MvpAppCompatFragment;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
@@ -16,6 +16,7 @@ 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;
@@ -25,8 +26,8 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
* Created by Konstantin Tskhovrebov (aka @terrakok) on 14.07.17.
*/
public class SelectPhotoFragment extends MvpAppCompatFragment implements SelectPhotoView, BackButtonListener {
private static final String ARG_RESULT_CODE = "arg_result_code";
public class SelectPhotoFragment extends MvpAppCompatFragment
implements SelectPhotoView, BackButtonListener {
private static final String ARG_ANIM_DESTINATION = "arg_anim_dest";
private ImageView photo1;
@@ -37,19 +38,15 @@ public class SelectPhotoFragment extends MvpAppCompatFragment implements SelectP
@Inject
Router router;
@Inject
PhotoSelection photoSelection;
@InjectPresenter
SelectPhotoPresenter presenter;
public static SelectPhotoFragment getNewInstance(int resultCode) {
SelectPhotoFragment fragment = new SelectPhotoFragment();
Bundle args = new Bundle();
args.putInt(ARG_RESULT_CODE, resultCode);
fragment.setArguments(args);
return fragment;
}
public void setAnimationDestinationId(int resId) {
Bundle arguments = getArguments();
if (arguments == null) arguments = new Bundle();
arguments.putInt(ARG_ANIM_DESTINATION, resId);
setArguments(arguments);
}
@@ -60,7 +57,7 @@ public class SelectPhotoFragment extends MvpAppCompatFragment implements SelectP
@ProvidePresenter
SelectPhotoPresenter providePresenter() {
return new SelectPhotoPresenter(router, getArguments().getInt(ARG_RESULT_CODE));
return new SelectPhotoPresenter(photoSelection, router);
}
@Override
@@ -1,12 +1,12 @@
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 androidx.annotation.Nullable;
import com.arellomobile.mvp.MvpAppCompatFragment;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
@@ -16,6 +16,7 @@ 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;
@@ -34,9 +35,12 @@ public class ProfileFragment extends MvpAppCompatFragment implements ProfileView
@InjectPresenter
ProfilePresenter presenter;
@Inject
PhotoSelection photoSelection;
@ProvidePresenter
ProfilePresenter providePresenter() {
return new ProfilePresenter(R.drawable.ava_1, router);
return new ProfilePresenter(photoSelection, router);
}
@Override
@@ -1,9 +1,9 @@
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 androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.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;
@@ -31,18 +27,13 @@ import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
/**
* Created by terrakok 25.11.16
*/
public class BottomNavigationActivity extends MvpAppCompatActivity implements BottomNavigationView, 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 +51,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 +67,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 +93,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 onResumeFragments() {
super.onResumeFragments();
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,52 +146,6 @@ public class BottomNavigationActivity extends MvpAppCompatActivity implements Bo
}
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommands(Command[] commands) {
for (Command command : commands) applyCommand(command);
}
private void applyCommand(Command command) {
if (command instanceof Back) {
finish();
} else if (command instanceof SystemMessage) {
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;
@@ -1,13 +1,13 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import com.arellomobile.mvp.MvpAppCompatFragment;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
@@ -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,11 +1,8 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -15,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;
@@ -69,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));
}
}
@@ -87,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(Context context, String screenKey, Object data) {
if (screenKey.equals(Screens.GITHUB_SCREEN)) {
return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"));
}
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;
}
@@ -0,0 +1,39 @@
package ru.terrakok.cicerone.sample.ui.main;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.arellomobile.mvp.MvpAppCompatFragment;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
public abstract class BaseFragment extends MvpAppCompatFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
FragmentActivity activity = getActivity();
if (activity instanceof ChainHolder) {
((ChainHolder) activity).getChain().add(new WeakReference<Fragment>(this));
}
}
@Override
public void onDetach() {
FragmentActivity activity = getActivity();
if (activity instanceof ChainHolder) {
List<WeakReference<Fragment>> chain = ((ChainHolder) activity).getChain();
for (Iterator<WeakReference<Fragment>> it = chain.iterator(); it.hasNext();) {
WeakReference<Fragment> fragmentReference = it.next();
Fragment fragment = fragmentReference.get();
if (fragment != null && fragment == this) {
it.remove();
break;
}
}
}
super.onDetach();
}
}
@@ -0,0 +1,10 @@
package ru.terrakok.cicerone.sample.ui.main;
import androidx.fragment.app.Fragment;
import java.lang.ref.WeakReference;
import java.util.List;
public interface ChainHolder {
List<WeakReference<Fragment>> getChain();
}
@@ -1,24 +1,22 @@
package ru.terrakok.cicerone.sample.ui.main;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.widget.TextView;
import android.widget.Toast;
import com.arellomobile.mvp.MvpAppCompatActivity;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.List;
import javax.inject.Inject;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.android.SupportFragmentNavigator;
import ru.terrakok.cicerone.android.support.SupportAppNavigator;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.sample.R;
@@ -31,31 +29,14 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
* on 11.10.16
*/
public class MainActivity extends MvpAppCompatActivity {
private static final String STATE_SCREEN_NAMES = "state_screen_names";
private List<String> screenNames = new ArrayList<>();
public class MainActivity extends MvpAppCompatActivity implements ChainHolder {
private TextView screensSchemeTV;
private List<WeakReference<Fragment>> chain = new ArrayList<>();
@Inject
NavigatorHolder navigatorHolder;
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.main_container) {
@Override
public void applyCommands(Command[] commands) {
super.applyCommands(commands);
@@ -73,9 +54,8 @@ public class MainActivity extends MvpAppCompatActivity {
screensSchemeTV = (TextView) findViewById(R.id.screens_scheme);
if (savedInstanceState == null) {
navigator.applyCommands(new Command[]{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();
}
}
@@ -104,28 +84,33 @@ public class MainActivity extends MvpAppCompatActivity {
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(STATE_SCREEN_NAMES, (Serializable) screenNames);
}
private void printScreensScheme() {
ArrayList<Integer> keys = new ArrayList<>();
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof SampleFragment) {
keys.add(((SampleFragment) fragment).getNumber());
}
ArrayList<SampleFragment> fragments = new ArrayList<>();
for (WeakReference<Fragment> fragmentReference : chain) {
Fragment fragment = fragmentReference.get();
if (fragment != null && fragment instanceof SampleFragment) {
fragments.add((SampleFragment) fragment);
}
}
Collections.sort(keys, new Comparator<Integer>() {
Collections.sort(fragments, new Comparator<SampleFragment>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
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() + "");
}
@Override
public List<WeakReference<Fragment>> getChain() {
return chain;
}
}
@@ -1,13 +1,11 @@
package ru.terrakok.cicerone.sample.ui.main;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.arellomobile.mvp.MvpAppCompatFragment;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
@@ -25,8 +23,9 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
* on 11.10.16
*/
public class SampleFragment extends MvpAppCompatFragment implements SampleView, BackButtonListener {
public class SampleFragment extends BaseFragment 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 +33,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,6 +53,7 @@ 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;
@@ -63,6 +63,10 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView,
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);
@@ -84,9 +88,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
@@ -128,12 +132,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) {
@@ -146,6 +144,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,19 +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.animations.ProfileActivity;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
/**
* Created by terrakok 21.11.16
@@ -42,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);
@@ -93,66 +84,4 @@ public class StartActivity extends MvpAppCompatActivity implements StartActivity
public void onBackPressed() {
presenter.onBackPressed();
}
//Sample fully custom navigator:
private Navigator navigator = new Navigator() {
@Override
public void applyCommands(Command[] commands) {
for (Command command : commands) applyCommand(command);
}
private void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Replace) {
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;
case Screens.PROFILE_SCREEN:
startActivity(new Intent(StartActivity.this, ProfileActivity.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:
case Screens.PROFILE_SCREEN:
forward(new Forward(command.getScreenKey(), command.getTransitionData()));
finish();
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
}
}
private void back() {
finish();
}
};
}
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.Space
<androidx.legacy.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
@@ -18,7 +18,7 @@
android:text="@string/cicerone_description"
android:textSize="18sp"/>
<android.support.v4.widget.Space
<androidx.legacy.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
@@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
@@ -15,7 +15,7 @@
app:navigationIcon="@drawable/ic_arrow_back_white_24dp"
app:title="@string/app_name"/>
<android.support.v4.widget.Space
<androidx.legacy.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
@@ -48,7 +48,7 @@
android:text="github"
android:textSize="20sp"/>
<android.support.v4.widget.Space
<androidx.legacy.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
+44 -28
View File
@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
@@ -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>