39 Commits

Author SHA1 Message Date
terrakok bec83313c7 Merge branch 'develop' 2017-03-27 13:41:51 +03:00
terrakok d3398ae8e6 updated version to 1.2 2017-03-27 13:41:20 +03:00
terrakok 12e925cbec Merge branch 'develop' 2017-03-27 13:31:16 +03:00
terrakok 3e1d0fba75 updated readme files for new version 2017-03-27 13:30:49 +03:00
Konstantin a8bf9a331f Merge pull request #23 from Jeevuz/javadoc-change
Changed javadoc for unknownScreen()
2017-03-26 01:22:59 +03:00
jeevuz 2862752da9 Changed javadoc for unknownScreen() 2017-03-26 00:23:10 +03:00
terrakok ce90bbf617 updated library versions and actualized sample app 2017-03-25 17:01:16 +03:00
terrakok 237be41c07 completed new AppNavigator 2017-03-25 17:00:36 +03:00
Konstantin 5f1d9eb039 Merge pull request #22 from Jeevuz/app-navigators
Changed to use separate AppNavigators for work with Activities.
2017-03-24 13:30:16 +03:00
Vasili Chyrvon 920868c1f2 Changed to use separate AppNavigators for work with Activities.
Changed some javadocs.
Updated some lib versions.
2017-03-24 12:56:58 +03:00
terrakok ebcbf503cf update gradle plugin 2017-03-10 17:10:50 +03:00
terrakok af338d04b9 added demonstration launching external activity 2017-03-10 16:44:37 +03:00
terrakok 50eafba93a changed sample application for new navigator 2017-03-10 16:36:57 +03:00
terrakok 30c284b5a2 added new fragment navigator constructor for using child fragment manager case 2017-03-10 16:35:05 +03:00
terrakok 771f837e64 created default implementation for completed fragment navigator methods: exit() and showSystemMessage() 2017-03-10 16:19:14 +03:00
terrakok ecb8b1a430 Update README.md 2017-02-27 20:35:10 +04:00
terrakok 2836c63126 added ability to launch new Activities 2017-02-22 19:51:12 +03:00
terrakok de7422144b added method finishChain for Router 2017-01-29 00:11:40 +03:00
terrakok b044d560f2 updated readme.md 2016-11-28 19:16:08 +03:00
terrakok 39d9657c8c Merge branch 'ft_child_navigation' into develop 2016-11-28 00:13:55 +03:00
terrakok 5033949e48 some refactor 2016-11-28 00:13:26 +03:00
terrakok 60beb90e11 fixed container initialization after activity rotation 2016-11-26 13:30:37 +03:00
terrakok 4226595a71 implemented local navigation for each tab 2016-11-26 00:54:15 +03:00
terrakok ed837a0c43 added activity with bottom navigation bar 2016-11-25 01:22:19 +03:00
terrakok 5518c16789 integrated Dagger 2 2016-11-24 23:26:46 +03:00
terrakok 96ae7361f8 updated Moxy and Support library version 2016-11-22 23:15:25 +03:00
terrakok a7e713865a added Android Arsenal badge and fixed jCenter badge 2016-11-22 22:52:02 +03:00
terrakok 427592e309 Merge branch 'develop' 2016-11-22 18:52:45 +03:00
terrakok b3e32c154f Merge pull request #13 from Jeevuz/develop
Upgrade to version 1.1
2016-11-22 19:51:08 +04:00
Vasili Chyrvon 69feb03db6 Upgrade to version 1.1 2016-11-22 18:39:31 +03:00
terrakok 1505ba87af Merge pull request #12 from Jeevuz/develop
Bages and new How to add part
2016-11-22 19:14:35 +04:00
Vasili Chyrvon 3dfb107a21 Changed russian readme 2016-11-22 18:10:38 +03:00
Vasili Chyrvon 458d9b053e Added badges 2016-11-22 18:04:58 +03:00
Vasili Chyrvon 93cecdee1d Published on jcenter. 2016-11-22 17:25:05 +03:00
terrakok 457c70e026 Merge pull request #10 from laomo/develop
Add app stub and remove android dependency
2016-11-22 12:38:39 +04:00
laomo e88b08d8ce Add app stub and remove android dependency 2016-11-22 16:04:49 +08:00
terrakok 4e896d3631 added the startup screen to show the transitions between Activities 2016-11-21 23:28:04 +03:00
terrakok a5404b0939 Merge pull request #7 from Jeevuz/patch-1
Update README.md
2016-11-20 08:12:40 +04:00
Vasili Chyrvon 4d096351cc Update README.md 2016-11-20 01:35:19 +03:00
64 changed files with 1544 additions and 123 deletions
+60 -43
View File
@@ -1,31 +1,36 @@
# Cicerone
[![jCenter](https://api.bintray.com/packages/terrakok/terramaven/cicerone/images/download.svg)](https://bintray.com/terrakok/terramaven/cicerone/_latestVersion)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Cicerone-green.svg?style=true)](https://android-arsenal.com/details/1/4700)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-250-lightgrey.svg)](http://androidweekly.net/issues/issue-250)
[![Telegram](https://img.shields.io/badge/Telegram-ENG-blue.svg)](https://t.me/Cicerone_ENG)
[![Telegram](https://img.shields.io/badge/Telegram-RUS-blue.svg)](https://t.me/Cicerone_RUS)
Cicerone (a guide who gives information to sightseers) is a lightweight library that makes the navigation in an Android app easy.
It designed for using with MVP architecture (try [Moxy](https://github.com/Arello-Mobile/Moxy)), but it fits to work in other ways.
![](https://habrastorage.org/files/644/32e/9eb/64432e9eb3664723b3ee438449dab3b0.png)
Cicerone (a guide, one who conducts sightseers) is a lightweight library that makes the navigation in an Android app easy.
It was designed to be used with the MVP pattern (try [Moxy](https://github.com/Arello-Mobile/Moxy)), but will work great with any architecture.
[Russian version readme](https://github.com/terrakok/Cicerone/blob/develop/README_RUS.md)
## Main advantages
+ not tied to Fragments
+ not framework
+ is not tied to Fragments
+ not a framework
+ short navigation calls (no builders)
+ lifecycle-safely!
+ functional is simple to extent
+ adapted for Unit Testing
+ lifecycle-safe!
+ functionality is simple to extend
+ suitable for Unit Testing
## How to connect?
Add the following lines to build.gradle:
## How to add
Add the dependency in your build.gradle:
```groovy
repositories {
maven {
url 'https://dl.bintray.com/terrakok/terramaven/'
}
}
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:1.0'
compile 'ru.terrakok.cicerone:cicerone:X.X'
}
```
And initialise library for example with application:
Initialize the library (for example in your Application class):
```java
public class SampleApplication extends MvpApplication {
public static SampleApplication INSTANCE;
@@ -76,10 +81,11 @@ public class SamplePresenter extends Presenter<SampleView> {
}
```
Router converts the navigation calls to Comand sets and sends them to CommandBuffer.
Command Buffer checks whether there _"active"_ Navigator.
If yes, it caused the necessary commands for the requested transfer.
If not, the command added to the queue, which will be applied as soon as _"active"_ Navigator.
Router converts the navigation call to the set of Commands and sends them to CommandBuffer.
CommandBuffer checks whether there are _"active"_ Navigator:
If yes, it passes the commands to the Navigator. Navigator will process them to achive the desired transition.
If no, then CommandBuffer saves the commands in a queue, and will apply them as soon as new _"active"_ Navigator will appear.
```java
protected void executeCommand(Command command) {
@@ -91,8 +97,8 @@ protected void executeCommand(Command command) {
}
```
Navigator - implements the navigation commands, e.g. anonymous class inside the Activity.
Activity provides Navigator for CommandBuffer in _onResume_ and remove in _onPause_
Navigator processes the navigation commands. Usually it is an anonymous class inside the Activity.
Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.
```java
@Override
@@ -110,27 +116,27 @@ protected void onPause() {
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
//implements commands logic
//implement commands logic
}
};
```
## Navigation commands
These command set will fulfill needs of the most application. But if you require more - supply your own!
This commands set will fulfill the needs of the most applications. But if you need something special - just add it!
+ Forward - Opens new screen
![](https://habrastorage.org/files/862/77e/b20/86277eb20b574dae8307ac4f64b0f090.png)
+ Back - Rolls back the last transition from the screens chain
+ Back - Rolls back the last transition
![](https://habrastorage.org/files/059/b63/2d3/059b632d3a7c4515a534b9e5e881c8f0.png)
+ BackTo - Rolls back to the needed screen from the screens chain
+ BackTo - Rolls back to the needed screen in the screens chain
![](https://habrastorage.org/files/a45/4f4/c34/a454f4c340764632ad0669014ad5550d.png)
+ Replace - Replaces the current screen
![](https://habrastorage.org/files/4ae/95c/fee/4ae95cfee4c04f038ad17d358ab08d07.png)
+ SystemMessage - Shows system message (Alert, Toast, Snack etc)
+ SystemMessage - Shows system message (Alert, Toast, Snack, etc.)
![](https://habrastorage.org/files/6e7/1a6/4ed/6e71a64edec04079bf33faa7ab39606f.png)
## Ready navigators
The library provides ready navigators for _Activity_.
To use them, just give the container and pass _FragmentManager_.
## 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.
```java
private Navigator navigator = new SupportFragmentNavigator(
getSupportFragmentManager(), R.id.main_container) {
@@ -151,24 +157,35 @@ private Navigator navigator = new SupportFragmentNavigator(
};
```
## Sample
Library usage examples, ready navigators and more can be found in the sample application.
To see how to add, initialize and use the library and predefined navigators check out the sample.
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
## Participants
+ idea and realization - Konstantin Tckhovrebov (@terrakok)
+ architectural advice, documentation and publication - Vasily Chirvon (@Jeevuz)
+ idea and code - Konstantin Tskhovrebov (@terrakok)
+ architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)
## License
```
The MIT License (MIT)
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
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
http://www.apache.org/licenses/LICENSE-2.0
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
+4 -8
View File
@@ -12,17 +12,11 @@ Cicerone (_"чи-че-ро́-не"_ - устар. гид) - легкая биб
+ приспособлена для Unit тестов
## Как подключить?
Добавьте в build.gradle следующие строки:
Добавьте в build.gradle зависимость:
```groovy
repositories {
maven {
url 'https://dl.bintray.com/terrakok/terramaven/'
}
}
dependencies {
//Cicerone
compile 'ru.terrakok.cicerone:cicerone:1.0'
compile 'ru.terrakok.cicerone:cicerone:X.X'
}
```
И инициализируйте библиотеку, например, так:
@@ -157,6 +151,8 @@ private Navigator navigator = new SupportFragmentNavigator(
![](https://habrastorage.org/files/16d/2ee/6e3/16d2ee6e33a0428eb4f0dcab8ce6b294.gif)
![](https://hsto.org/files/867/638/c33/867638c338704489b3107a6d7cb28c2d.gif)
## Участники
+ идея и реализация - Константин Цховребов (@terrakok)
+ архитектурные советы, документация и публикация - Василий Чирвон (@Jeevuz)
+1 -1
View File
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.android.tools.build:gradle:2.3.0'
// For the library uploading to the Bintray
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1'
+2 -2
View File
@@ -1,6 +1,6 @@
#Tue Sep 06 14:31:17 MSK 2016
#Fri Mar 10 17:09:31 MSK 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+3 -4
View File
@@ -6,8 +6,7 @@ sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly 'com.google.android:android:4.0.1.2'
compileOnly project(':stub-appcompat')
compileOnly project(':stub-android')
}
ext {
@@ -16,9 +15,9 @@ ext {
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
artifact = 'cicerone'
libraryVersion = '1.0'
libraryVersion = '1.2'
gitUrl = 'https://github.com/terrakok/Cicerone'
allLicenses = ['Apache-2.0']
allLicenses = ['MIT']
}
// Configuration of the library uploading to the Bintray
@@ -120,6 +120,15 @@ public class Router extends BaseRouter {
executeCommand(new BackTo(screenKey));
}
/**
* Remove all screens from the chain and exit.
* It's mostly used to finish the application or close a supplementary navigation chain.
*/
public void finishChain() {
executeCommand(new BackTo(null));
executeCommand(new Back());
}
/**
* Return to the previous screen in the chain.
* Behavior in the case when the current screen is the root depends on
@@ -0,0 +1,88 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link FragmentNavigator} to allow
* open new or replace current activity.
* <p>
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class AppNavigator extends FragmentNavigator {
private Activity activity;
public AppNavigator(Activity activity, int containerId) {
super(activity.getFragmentManager(), containerId);
this.activity = activity;
}
public AppNavigator(Activity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -33,8 +33,9 @@ public abstract class FragmentNavigator implements Navigator {
/**
* Creates FragmentNavigator.
*
* @param fragmentManager fragment manager
* @param containerId id of the fragments container layout
* @param containerId id of the fragments container layout
*/
public FragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
@@ -45,9 +46,14 @@ public abstract class FragmentNavigator implements Navigator {
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(forward.getScreenKey(), forward.getTransitionData()))
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
@@ -58,17 +64,22 @@ public abstract class FragmentNavigator implements Navigator {
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(replace.getScreenKey(), replace.getTransitionData()))
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(replace.getScreenKey(), replace.getTransitionData()))
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
@@ -103,14 +114,16 @@ public abstract class FragmentNavigator implements Navigator {
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @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);
@@ -126,4 +139,11 @@ public abstract class FragmentNavigator implements Navigator {
protected void backToUnexisting() {
backToRoot();
}
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
}
}
@@ -0,0 +1,89 @@
package ru.terrakok.cicerone.android;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Extends {@link SupportFragmentNavigator} to allow
* open new or replace current activity.
* <p>
* This navigator DOESN'T provide full featured Activity navigation,
* but can ease Activity start or replace from current navigator.
* </p>
*
* @author Vasili Chyrvon (vasili.chyrvon@gmail.com)
*/
public abstract class SupportAppNavigator extends SupportFragmentNavigator {
private Activity activity;
public SupportAppNavigator(FragmentActivity activity, int containerId) {
super(activity.getSupportFragmentManager(), containerId);
this.activity = activity;
}
public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) {
super(fragmentManager, containerId);
this.activity = activity;
}
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Intent activityIntent = createActivityIntent(forward.getScreenKey(), forward.getTransitionData());
// Start activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
return;
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Intent activityIntent = createActivityIntent(replace.getScreenKey(), replace.getTransitionData());
// Replace activity
if (activityIntent != null) {
activity.startActivity(activityIntent);
activity.finish();
return;
}
}
// Use default fragments navigation
super.applyCommand(command);
}
/**
* Creates Intent to start Activity for {@code screenKey}.
* <p>
* <b>Warning:</b> This method does not work with {@link BackTo} command.
* </p>
*
* @param screenKey screen key
* @param data initialization data, can be null
* @return intent to start Activity for the passed screen key
*/
protected abstract Intent createActivityIntent(String screenKey, Object data);
@Override
protected void showSystemMessage(String message) {
// Toast by default
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
@Override
protected void exit() {
// Finish by default
activity.finish();
}
}
@@ -33,8 +33,9 @@ public abstract class SupportFragmentNavigator implements Navigator {
/**
* Creates SupportFragmentNavigator.
*
* @param fragmentManager support fragment manager
* @param containerId id of the fragments container layout
* @param containerId id of the fragments container layout
*/
public SupportFragmentNavigator(FragmentManager fragmentManager, int containerId) {
this.fragmentManager = fragmentManager;
@@ -45,9 +46,14 @@ public abstract class SupportFragmentNavigator implements Navigator {
public void applyCommand(Command command) {
if (command instanceof Forward) {
Forward forward = (Forward) command;
Fragment fragment = createFragment(forward.getScreenKey(), forward.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(forward.getScreenKey(), forward.getTransitionData()))
.replace(containerId, fragment)
.addToBackStack(forward.getScreenKey())
.commit();
} else if (command instanceof Back) {
@@ -58,17 +64,22 @@ public abstract class SupportFragmentNavigator implements Navigator {
}
} else if (command instanceof Replace) {
Replace replace = (Replace) command;
Fragment fragment = createFragment(replace.getScreenKey(), replace.getTransitionData());
if (fragment == null) {
unknownScreen(command);
return;
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStackImmediate();
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(replace.getScreenKey(), replace.getTransitionData()))
.replace(containerId, fragment)
.addToBackStack(replace.getScreenKey())
.commit();
} else {
fragmentManager
.beginTransaction()
.replace(containerId, createFragment(replace.getScreenKey(), replace.getTransitionData()))
.replace(containerId, fragment)
.commit();
}
} else if (command instanceof BackTo) {
@@ -103,14 +114,16 @@ public abstract class SupportFragmentNavigator implements Navigator {
/**
* Creates Fragment matching {@code screenKey}.
*
* @param screenKey screen key
* @param data initialization data
* @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);
@@ -126,4 +139,10 @@ public abstract class SupportFragmentNavigator implements Navigator {
protected void backToUnexisting() {
backToRoot();
}
}
/**
* Called if we can't create a screen.
*/
protected void unknownScreen(Command command) {
}
}
@@ -2,7 +2,3 @@ apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly 'com.google.android:android:4.0.1.2'
}
@@ -0,0 +1,21 @@
package android.app;
import android.content.Context;
import android.content.Intent;
/***
* Created by laomo on 2016-11-21.
*/
public class Activity extends Context {
public void startActivity(Intent intent) {
throw new RuntimeException("Stub!");
}
public FragmentManager getFragmentManager() {
throw new RuntimeException("Stub!");
}
public void finish() {
throw new RuntimeException("Stub!");
}
}
@@ -0,0 +1,36 @@
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!");
}
}
@@ -0,0 +1,41 @@
package android.app;
/***
* Created by laomo on 2016-11-21.
*/
public class FragmentManager {
public FragmentTransaction beginTransaction() {
throw new RuntimeException("Stub!");
}
public boolean executePendingTransactions() {
throw new RuntimeException("Stub!");
}
public void popBackStack() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate() {
throw new RuntimeException("Stub!");
}
public boolean popBackStackImmediate(String name, int flags) {
throw new RuntimeException("Stub!");
}
public int getBackStackEntryCount() {
throw new RuntimeException("Stub!");
}
public BackStackEntry getBackStackEntryAt(int index) {
throw new RuntimeException("Stub!");
}
public interface BackStackEntry {
int getId();
String getName();
}
}
@@ -0,0 +1,18 @@
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!");
}
}
@@ -0,0 +1,8 @@
package android.content;
/**
* @author Konstantin Tskhovrebov (aka terrakok). Date: 10.03.17
*/
public class Context {
}
@@ -0,0 +1,9 @@
package android.content;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 22.02.17
*/
public class Intent {
}
@@ -0,0 +1,7 @@
package android.os;
/***
* Created by laomo on 2016-11-21.
*/
public class Bundle {
}
@@ -8,4 +8,7 @@ import android.app.Activity;
*/
public class FragmentActivity extends Activity {
public FragmentManager getSupportFragmentManager() {
throw new RuntimeException("Stub!");
}
}
@@ -0,0 +1,20 @@
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!");
}
}
+17 -7
View File
@@ -5,12 +5,12 @@ repositories {
}
android {
compileSdkVersion 24
buildToolsVersion '24'
compileSdkVersion 25
buildToolsVersion '25.0.2'
defaultConfig {
minSdkVersion 15
targetSdkVersion 24
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0.0"
applicationId "ru.terrakok.cicerone.sample"
@@ -28,20 +28,30 @@ android {
}
ext {
supportLibraryVersion = "24.2.1"
moxyVersion = "1.0.2"
supportLibraryVersion = "25.3.0"
moxyVersion = "1.4.6"
daggerVersion = "2.10"
}
dependencies {
// Support libraries
compile "com.android.support:appcompat-v7:$supportLibraryVersion"
compile "com.android.support:design:$supportLibraryVersion"
//MVP Moxy
compile "com.arello-mobile:moxy:$moxyVersion"
compile "com.arello-mobile:moxy-android:$moxyVersion"
compile "com.arello-mobile:moxy-app-compat:$moxyVersion"
provided "com.arello-mobile:moxy-compiler:$moxyVersion"
//Cicerone
compile project(':library')
//DI
compile "com.google.dagger:dagger:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
//Bottom Navigation Bar
compile ('com.ashokvarma.android:bottom-navigation-bar:1.3.0') {
exclude group: "com.android.support", module: "design"
}
}
+7 -4
View File
@@ -5,17 +5,20 @@
<application
android:name=".SampleApplication"
android:allowBackup="false"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme"
android:label="@string/app_name">
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".ui.main.MainActivity">
android:name=".ui.start.StartActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ui.main.MainActivity"/>
<activity android:name=".ui.bottom.BottomNavigationActivity"/>
</application>
</manifest>
@@ -1,37 +1,29 @@
package ru.terrakok.cicerone.sample;
import com.arellomobile.mvp.MvpApplication;
import android.app.Application;
import ru.terrakok.cicerone.Cicerone;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.dagger.AppComponent;
import ru.terrakok.cicerone.sample.dagger.DaggerAppComponent;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
public class SampleApplication extends MvpApplication {
public class SampleApplication extends Application {
public static SampleApplication INSTANCE;
private Cicerone<Router> cicerone;
private AppComponent appComponent;
@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();
public AppComponent getAppComponent() {
if (appComponent == null) {
appComponent = DaggerAppComponent.builder().build();
}
return appComponent;
}
}
@@ -7,4 +7,15 @@ package ru.terrakok.cicerone.sample;
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 String BOTTOM_NAVIGATION_ACTIVITY_SCREEN = "bna screen";
public static final String ANDROID_SCREEN = "android screen";
public static final String BUG_SCREEN = "bug screen";
public static final String DOG_SCREEN = "dog screen";
public static final String FORWARD_SCREEN = "forward screen";
public static final String GITHUB_SCREEN = "github screen";
}
@@ -0,0 +1,34 @@
package ru.terrakok.cicerone.sample.dagger;
import javax.inject.Singleton;
import dagger.Component;
import ru.terrakok.cicerone.sample.dagger.module.LocalNavigationModule;
import ru.terrakok.cicerone.sample.dagger.module.NavigationModule;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.bottom.TabContainerFragment;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
import ru.terrakok.cicerone.sample.ui.main.SampleFragment;
import ru.terrakok.cicerone.sample.ui.start.StartActivity;
/**
* Created by terrakok 24.11.16
*/
@Singleton
@Component(modules = {
NavigationModule.class,
LocalNavigationModule.class
})
public interface AppComponent {
void inject(StartActivity activity);
void inject(MainActivity activity);
void inject(SampleFragment fragment);
void inject(BottomNavigationActivity activity);
void inject(TabContainerFragment fragment);
}
@@ -0,0 +1,21 @@
package ru.terrakok.cicerone.sample.dagger.module;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import ru.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder;
/**
* Created by terrakok 24.11.16
*/
@Module
public class LocalNavigationModule {
@Provides
@Singleton
LocalCiceroneHolder provideLocalNavigationHolder() {
return new LocalCiceroneHolder();
}
}
@@ -0,0 +1,34 @@
package ru.terrakok.cicerone.sample.dagger.module;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import ru.terrakok.cicerone.Cicerone;
import ru.terrakok.cicerone.NavigatorHolder;
import ru.terrakok.cicerone.Router;
/**
* Created by terrakok 24.11.16
*/
@Module
public class NavigationModule {
private Cicerone<Router> cicerone;
public NavigationModule() {
cicerone = Cicerone.create();
}
@Provides
@Singleton
Router provideRouter() {
return cicerone.getRouter();
}
@Provides
@Singleton
NavigatorHolder provideNavigatorHolder() {
return cicerone.getNavigatorHolder();
}
}
@@ -0,0 +1,38 @@
package ru.terrakok.cicerone.sample.mvp.bottom;
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
*/
@InjectViewState
public class BottomNavigationPresenter extends MvpPresenter<BottomNavigationView> {
private Router router;
public BottomNavigationPresenter(Router router) {
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();
}
}
@@ -0,0 +1,17 @@
package ru.terrakok.cicerone.sample.mvp.bottom;
import com.arellomobile.mvp.MvpView;
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
/**
* Created by terrakok 25.11.16
*/
@StateStrategyType(AddToEndSingleStrategy.class)
public interface BottomNavigationView extends MvpView {
int ANDROID_TAB_POSITION = 0;
int BUG_TAB_POSITION = 1;
int DOG_TAB_POSITION = 2;
void highlightTab(int position);
}
@@ -0,0 +1,46 @@
package ru.terrakok.cicerone.sample.mvp.bottom.forward;
import com.arellomobile.mvp.InjectViewState;
import com.arellomobile.mvp.MvpPresenter;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.Screens;
/**
* Created by terrakok 26.11.16
*/
@InjectViewState
public class ForwardPresenter extends MvpPresenter<ForwardView> {
private Router router;
private int number;
public ForwardPresenter(Router router, int number) {
this.router = router;
this.number = number;
getViewState().setChainText(createChain(number));
}
private String createChain(int number) {
String chain = "[0]";
for (int i = 0; i < number; i++) {
chain += "" + (i + 1);
}
return chain;
}
public void onForwardPressed() {
router.navigateTo(Screens.FORWARD_SCREEN, number + 1);
}
public void onGithubPressed() {
router.navigateTo(Screens.GITHUB_SCREEN);
}
public void onBackPressed() {
router.exit();
}
}
@@ -0,0 +1,14 @@
package ru.terrakok.cicerone.sample.mvp.bottom.forward;
import com.arellomobile.mvp.MvpView;
import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
/**
* Created by terrakok 26.11.16
*/
@StateStrategyType(AddToEndSingleStrategy.class)
public interface ForwardView extends MvpView {
void setChainText(String chainText);
}
@@ -9,7 +9,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
/**
@@ -24,18 +23,11 @@ public class SamplePresenter extends MvpPresenter<SampleView> {
private ScheduledExecutorService executorService;
private ScheduledFuture<?> future;
public SamplePresenter() {
router = SampleApplication.INSTANCE.getRouter();
executorService = Executors.newSingleThreadScheduledExecutor();
}
public void init(int screenNumber) {
public SamplePresenter(Router router, int screenNumber) {
this.router = router;
this.screenNumber = screenNumber;
}
executorService = Executors.newSingleThreadScheduledExecutor();
@Override
protected void onFirstViewAttach() {
super.onFirstViewAttach();
getViewState().setTitle("Screen " + screenNumber);
}
@@ -0,0 +1,29 @@
package ru.terrakok.cicerone.sample.mvp.start;
import com.arellomobile.mvp.MvpPresenter;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.Screens;
/**
* Created by terrakok 21.11.16
*/
public class StartActivityPresenter extends MvpPresenter<StartActivityView> {
private Router router;
public StartActivityPresenter(Router router) {
this.router = router;
}
public void onOrdinaryPressed() {
router.navigateTo(Screens.MAIN_ACTIVITY_SCREEN);
}
public void onMultiPressed() {
router.navigateTo(Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN);
}
public void onBackPressed() {
router.exit();
}
}
@@ -0,0 +1,9 @@
package ru.terrakok.cicerone.sample.mvp.start;
import com.arellomobile.mvp.MvpView;
/**
* Created by terrakok 21.11.16
*/
public interface StartActivityView extends MvpView {
}
@@ -0,0 +1,24 @@
package ru.terrakok.cicerone.sample.subnavigation;
import java.util.HashMap;
import ru.terrakok.cicerone.Cicerone;
import ru.terrakok.cicerone.Router;
/**
* Created by terrakok 27.11.16
*/
public class LocalCiceroneHolder {
private HashMap<String, Cicerone<Router>> containers;
public LocalCiceroneHolder() {
containers = new HashMap<>();
}
public Cicerone<Router> getCicerone(String containerTag) {
if (!containers.containsKey(containerTag)) {
containers.put(containerTag, Cicerone.create());
}
return containers.get(containerTag);
}
}
@@ -0,0 +1,202 @@
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 com.arellomobile.mvp.MvpAppCompatActivity;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
import com.ashokvarma.bottomnavigation.BottomNavigationBar;
import com.ashokvarma.bottomnavigation.BottomNavigationItem;
import 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;
import ru.terrakok.cicerone.sample.mvp.bottom.BottomNavigationPresenter;
import ru.terrakok.cicerone.sample.mvp.bottom.BottomNavigationView;
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
/**
* Created by terrakok 25.11.16
*/
public class BottomNavigationActivity extends MvpAppCompatActivity implements BottomNavigationView, RouterProvider {
private BottomNavigationBar bottomNavigationBar;
private TabContainerFragment androidTabFragment;
private TabContainerFragment bugTabFragment;
private TabContainerFragment dogTabFragment;
@Inject
Router router;
@Inject
NavigatorHolder navigatorHolder;
@InjectPresenter
BottomNavigationPresenter presenter;
@ProvidePresenter
public BottomNavigationPresenter createBottomNavigationPresenter() {
return new BottomNavigationPresenter(router);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom);
bottomNavigationBar = (BottomNavigationBar) findViewById(R.id.ab_bottom_navigation_bar);
initViews();
initContainers();
if (savedInstanceState == null) {
bottomNavigationBar.selectTab(ANDROID_TAB_POSITION, true);
}
}
private void initViews() {
bottomNavigationBar
.addItem(new BottomNavigationItem(R.drawable.ic_android_white_24dp, R.string.tab_android))
.addItem(new BottomNavigationItem(R.drawable.ic_bug_report_white_24dp, R.string.tab_bug))
.addItem(new BottomNavigationItem(R.drawable.ic_pets_white_24dp, R.string.tab_dog))
.initialise();
bottomNavigationBar.setTabSelectedListener(new BottomNavigationBar.OnTabSelectedListener() {
@Override
public void onTabSelected(int position) {
switch (position) {
case ANDROID_TAB_POSITION:
presenter.onTabAndroidClick();
break;
case BUG_TAB_POSITION:
presenter.onTabBugClick();
break;
case DOG_TAB_POSITION:
presenter.onTabDogClick();
break;
}
}
@Override
public void onTabUnselected(int position) {
}
@Override
public void onTabReselected(int position) {
onTabSelected(position);
}
});
}
private void initContainers() {
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();
}
bugTabFragment = (TabContainerFragment) fm.findFragmentByTag("BUG");
if (bugTabFragment == null) {
bugTabFragment = TabContainerFragment.getNewInstance("BUG");
fm.beginTransaction()
.add(R.id.ab_container, bugTabFragment, "BUG")
.detach(bugTabFragment).commitNow();
}
dogTabFragment = (TabContainerFragment) fm.findFragmentByTag("DOG");
if (dogTabFragment == null) {
dogTabFragment = TabContainerFragment.getNewInstance("DOG");
fm.beginTransaction()
.add(R.id.ab_container, dogTabFragment, "DOG")
.detach(dogTabFragment).commitNow();
}
}
@Override
protected void onResume() {
super.onResume();
navigatorHolder.setNavigator(navigator);
}
@Override
protected void onPause() {
navigatorHolder.removeNavigator();
super.onPause();
}
@Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.ab_container);
if (fragment != null
&& fragment instanceof BackButtonListener
&& ((BackButtonListener) fragment).onBackPressed()) {
return;
} else {
presenter.onBackPressed();
}
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
if (command instanceof Back) {
finish();
} else if (command instanceof SystemMessage) {
Toast.makeText(BottomNavigationActivity.this, ((SystemMessage) command).getMessage(), Toast.LENGTH_SHORT).show();
} else if (command instanceof Replace) {
FragmentManager fm = getSupportFragmentManager();
switch (((Replace) command).getScreenKey()) {
case Screens.ANDROID_SCREEN:
fm.beginTransaction()
.detach(bugTabFragment)
.detach(dogTabFragment)
.attach(androidTabFragment)
.commitNow();
break;
case Screens.BUG_SCREEN:
fm.beginTransaction()
.detach(androidTabFragment)
.detach(dogTabFragment)
.attach(bugTabFragment)
.commitNow();
break;
case Screens.DOG_SCREEN:
fm.beginTransaction()
.detach(androidTabFragment)
.detach(bugTabFragment)
.attach(dogTabFragment)
.commitNow();
break;
}
}
}
};
@Override
public void highlightTab(int position) {
bottomNavigationBar.selectTab(position, false);
}
@Override
public Router getRouter() {
return router;
}
}
@@ -0,0 +1,104 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.arellomobile.mvp.MvpAppCompatFragment;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.mvp.bottom.forward.ForwardPresenter;
import ru.terrakok.cicerone.sample.mvp.bottom.forward.ForwardView;
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
/**
* Created by terrakok 26.11.16
*/
public class ForwardFragment extends MvpAppCompatFragment implements ForwardView, BackButtonListener {
private static final String EXTRA_NAME = "extra_name";
private static final String EXTRA_NUMBER = "extra_number";
private Toolbar toolbar;
private TextView chainTV;
private View forwardBt;
private View githubBt;
@InjectPresenter
ForwardPresenter presenter;
@ProvidePresenter
ForwardPresenter provideForwardPresenter() {
return new ForwardPresenter(
((RouterProvider) getParentFragment()).getRouter(),
getArguments().getInt(EXTRA_NUMBER)
);
}
public static ForwardFragment getNewInstance(String name, int number) {
ForwardFragment fragment = new ForwardFragment();
Bundle arguments = new Bundle();
arguments.putString(EXTRA_NAME, name);
arguments.putInt(EXTRA_NUMBER, number);
fragment.setArguments(arguments);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_forward, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
chainTV = (TextView) view.findViewById(R.id.chain_text);
forwardBt = view.findViewById(R.id.forward_button);
githubBt = view.findViewById(R.id.github_button);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
toolbar.setTitle(getArguments().getString(EXTRA_NAME));
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onBackPressed();
}
});
forwardBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onForwardPressed();
}
});
githubBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onGithubPressed();
}
});
}
@Override
public void setChainText(String chainText) {
chainTV.setText(chainText);
}
@Override
public boolean onBackPressed() {
presenter.onBackPressed();
return true;
}
}
@@ -0,0 +1,133 @@
package ru.terrakok.cicerone.sample.ui.bottom;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import ru.terrakok.cicerone.Cicerone;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.android.SupportAppNavigator;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
import ru.terrakok.cicerone.sample.subnavigation.LocalCiceroneHolder;
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
import ru.terrakok.cicerone.sample.ui.common.RouterProvider;
/**
* Created by terrakok 25.11.16
*/
public class TabContainerFragment extends Fragment implements RouterProvider, BackButtonListener {
private static final String EXTRA_NAME = "tcf_extra_name";
private Navigator navigator;
@Inject
LocalCiceroneHolder ciceroneHolder;
public static TabContainerFragment getNewInstance(String name) {
TabContainerFragment fragment = new TabContainerFragment();
Bundle arguments = new Bundle();
arguments.putString(EXTRA_NAME, name);
fragment.setArguments(arguments);
return fragment;
}
private String getContainerName() {
return getArguments().getString(EXTRA_NAME);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
super.onCreate(savedInstanceState);
}
private Cicerone<Router> getCicerone() {
return ciceroneHolder.getCicerone(getContainerName());
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab_container, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getChildFragmentManager().findFragmentById(R.id.ftc_container) == null) {
getCicerone().getRouter().replaceScreen(Screens.FORWARD_SCREEN, 0);
}
}
@Override
public void onResume() {
super.onResume();
getCicerone().getNavigatorHolder().setNavigator(getNavigator());
}
@Override
public void onPause() {
getCicerone().getNavigatorHolder().removeNavigator();
super.onPause();
}
private Navigator getNavigator() {
if (navigator == null) {
navigator = new SupportAppNavigator(getActivity(), getChildFragmentManager(), R.id.ftc_container) {
@Override
protected Intent createActivityIntent(String screenKey, Object data) {
if (screenKey.equals(Screens.GITHUB_SCREEN)) {
return new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/terrakok/Cicerone"));
}
return null;
}
@Override
protected Fragment createFragment(String screenKey, Object data) {
if (screenKey.equals(Screens.FORWARD_SCREEN)) {
return ForwardFragment.getNewInstance(getContainerName(), (int) data);
}
return null;
}
@Override
protected void exit() {
((RouterProvider) getActivity()).getRouter().exit();
}
};
}
return navigator;
}
@Override
public Router getRouter() {
return getCicerone().getRouter();
}
@Override
public boolean onBackPressed() {
Fragment fragment = getChildFragmentManager().findFragmentById(R.id.ftc_container);
if (fragment != null
&& fragment instanceof BackButtonListener
&& ((BackButtonListener) fragment).onBackPressed()) {
return true;
} else {
((RouterProvider) getActivity()).getRouter().exit();
return true;
}
}
}
@@ -0,0 +1,8 @@
package ru.terrakok.cicerone.sample.ui.common;
/**
* Created by terrakok 26.11.16
*/
public interface BackButtonListener {
boolean onBackPressed();
}
@@ -0,0 +1,10 @@
package ru.terrakok.cicerone.sample.ui.common;
import ru.terrakok.cicerone.Router;
/**
* Created by terrakok 25.11.16
*/
public interface RouterProvider {
Router getRouter();
}
@@ -12,7 +12,10 @@ import java.io.Serializable;
import java.util.ArrayList;
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.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
@@ -22,6 +25,7 @@ import ru.terrakok.cicerone.commands.Replace;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
@@ -30,9 +34,13 @@ import ru.terrakok.cicerone.sample.Screens;
public class MainActivity extends MvpAppCompatActivity {
private static final String STATE_SCREEN_NAMES = "state_screen_names";
private List<String> screenNames = new ArrayList<>();
private TextView screensSchemeTV;
@Inject
NavigatorHolder navigatorHolder;
private Navigator navigator = new SupportFragmentNavigator(getSupportFragmentManager(), R.id.main_container) {
@Override
protected Fragment createFragment(String screenKey, Object data) {
@@ -58,7 +66,9 @@ public class MainActivity extends MvpAppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screensSchemeTV = (TextView) findViewById(R.id.screens_scheme);
@@ -73,20 +83,22 @@ public class MainActivity extends MvpAppCompatActivity {
@Override
protected void onResume() {
super.onResume();
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(navigator);
navigatorHolder.setNavigator(navigator);
}
@Override
protected void onPause() {
navigatorHolder.removeNavigator();
super.onPause();
SampleApplication.INSTANCE.getNavigatorHolder().removeNavigator();
}
@Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
if (fragment != null && fragment instanceof SampleFragment) {
((SampleFragment) fragment).onBackPressed();
if (fragment != null
&& fragment instanceof BackButtonListener
&& ((BackButtonListener) fragment).onBackPressed()) {
return;
} else {
super.onBackPressed();
}
@@ -9,17 +9,23 @@ import android.view.ViewGroup;
import com.arellomobile.mvp.MvpAppCompatFragment;
import com.arellomobile.mvp.presenter.InjectPresenter;
import com.arellomobile.mvp.presenter.ProvidePresenter;
import javax.inject.Inject;
import ru.terrakok.cicerone.Router;
import ru.terrakok.cicerone.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.mvp.main.SamplePresenter;
import ru.terrakok.cicerone.sample.mvp.main.SampleView;
import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
/**
* Created by Konstantin Tckhovrebov (aka @terrakok)
* on 11.10.16
*/
public class SampleFragment extends MvpAppCompatFragment implements SampleView {
public class SampleFragment extends MvpAppCompatFragment implements SampleView, BackButtonListener {
private static final String EXTRA_NUMBER = "extra_number";
private Toolbar toolbar;
@@ -32,9 +38,17 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView {
private View forwardWithDelayCommandBt;
private View backToCommandBt;
@Inject
Router router;
@InjectPresenter
SamplePresenter presenter;
@ProvidePresenter
public SamplePresenter createSamplePresenter() {
return new SamplePresenter(router, getArguments().getInt(EXTRA_NUMBER));
}
public static SampleFragment getNewInstance(int number) {
SampleFragment fragment = new SampleFragment();
@@ -47,8 +61,8 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView {
@Override
public void onCreate(Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
super.onCreate(savedInstanceState);
presenter.init(getArguments().getInt(EXTRA_NUMBER));
}
@Nullable
@@ -135,7 +149,8 @@ public class SampleFragment extends MvpAppCompatFragment implements SampleView {
toolbar.setTitle(title);
}
public void onBackPressed() {
public boolean onBackPressed() {
presenter.onBackCommandClick();
return true;
}
}
@@ -0,0 +1,141 @@
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;
import com.arellomobile.mvp.presenter.ProvidePresenter;
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.sample.R;
import ru.terrakok.cicerone.sample.SampleApplication;
import ru.terrakok.cicerone.sample.Screens;
import ru.terrakok.cicerone.sample.mvp.start.StartActivityPresenter;
import ru.terrakok.cicerone.sample.mvp.start.StartActivityView;
import ru.terrakok.cicerone.sample.ui.bottom.BottomNavigationActivity;
import ru.terrakok.cicerone.sample.ui.main.MainActivity;
/**
* Created by terrakok 21.11.16
*/
public class StartActivity extends MvpAppCompatActivity implements StartActivityView {
@Inject
Router router;
@Inject
NavigatorHolder navigatorHolder;
@InjectPresenter
StartActivityPresenter presenter;
@ProvidePresenter
public StartActivityPresenter createStartActivityPresenter() {
return new StartActivityPresenter(router);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
SampleApplication.INSTANCE.getAppComponent().inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
initViews();
}
private void initViews() {
findViewById(R.id.ordinary_nav_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onOrdinaryPressed();
}
});
findViewById(R.id.multi_nav_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.onMultiPressed();
}
});
}
@Override
protected void onResume() {
super.onResume();
navigatorHolder.setNavigator(navigator);
}
@Override
protected void onPause() {
navigatorHolder.removeNavigator();
super.onPause();
}
@Override
public void onBackPressed() {
presenter.onBackPressed();
}
private Navigator navigator = new Navigator() {
@Override
public void applyCommand(Command command) {
if (command instanceof Forward) {
forward((Forward) command);
} else if (command instanceof Replace) {
replace((Replace) command);
} else if (command instanceof Back) {
back();
} else if (command instanceof SystemMessage) {
Toast.makeText(StartActivity.this, ((SystemMessage) command).getMessage(), Toast.LENGTH_SHORT).show();
} else {
Log.e("Cicerone", "Illegal command for this screen: " + command.getClass().getSimpleName());
}
}
private void forward(Forward command) {
switch (command.getScreenKey()) {
case Screens.START_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, StartActivity.class));
break;
case Screens.MAIN_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, MainActivity.class));
break;
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
startActivity(new Intent(StartActivity.this, BottomNavigationActivity.class));
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
}
}
private void replace(Replace command) {
switch (command.getScreenKey()) {
case Screens.START_ACTIVITY_SCREEN:
case Screens.MAIN_ACTIVITY_SCREEN:
case Screens.BOTTOM_NAVIGATION_ACTIVITY_SCREEN:
forward(new Forward(command.getScreenKey(), command.getTransitionData()));
finish();
break;
default:
Log.e("Cicerone", "Unknown screen: " + command.getScreenKey());
break;
}
}
private void back() {
finish();
}
};
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/ab_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/ab_bottom_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>
</LinearLayout>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp"
android:text="@string/cicerone_description"
android:textSize="18sp"/>
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="@+id/ordinary_nav_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/ordinary_nav"/>
<Button
android:id="@+id/multi_nav_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/multi_nav"/>
</LinearLayout>
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary"
app:navigationIcon="@drawable/ic_arrow_back_white_24dp"
app:title="@string/app_name"/>
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:id="@+id/chain_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp"
android:textColor="@android:color/white"
android:textSize="13sp"
tools:text="1➔2➔3➔4➔2➔3➔4➔2➔3➔4➔2➔3➔4➔2➔3➔4➔2➔3➔4"/>
<Button
android:id="@+id/forward_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minWidth="100dp"
android:text="➔"
android:textSize="20sp"/>
<Button
android:id="@+id/github_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minWidth="100dp"
android:text="github"
android:textSize="20sp"/>
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/ftc_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+7
View File
@@ -1,3 +1,10 @@
<resources>
<string name="app_name">Cicerone Sample</string>
<string name="cicerone_description">Cicerone is a lightweight library that makes the navigation in an Android app easy.</string>
<string name="ordinary_nav">Ordinary navigation</string>
<string name="multi_nav">Multi navigation</string>
<string name="tab_android">Android</string>
<string name="tab_bug">Bug</string>
<string name="tab_dog">Dog</string>
</resources>
+2 -2
View File
@@ -1,2 +1,2 @@
include ':library', ':sample', ':stub-appcompat'
project(':stub-appcompat').projectDir = new File('library/stub-appcompat')
include ':library', ':sample', ':stub-android'
project(':stub-android').projectDir = new File('library/stub-android')