25 Commits

Author SHA1 Message Date
terrakok 9aeb02cfae Update version to 5.1.1 2020-05-10 17:23:43 +03:00
Konstantin 7c31cdc1bd Merge pull request #112 from adolgiy/hotfix/fragment-factory-npe
Fix NPE when localStackCopy.size() == 0 and FragmentFactory is used
2020-05-10 17:11:14 +03:00
Aleksey Dolgiy 05a8ec8f44 Extract 'forward' to reuse in replace when localStackCopy.size > 0 2020-02-20 10:29:18 +03:00
Aleksey Dolgiy d08e6b1afe Code Review: Use internal replace for fragmentForward(Forward) and add explicit exception with null checks 2020-02-19 20:46:31 +03:00
Aleksey Dolgiy cea829ae88 Fix NPE when localStackCopy.size() == 0 and FragmentFactory is used 2020-02-19 17:37:25 +03:00
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
38 changed files with 387 additions and 161 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0-alpha12'
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'
+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 @@
#Thu Sep 27 15:42:22 MSK 2018
#Fri Jan 25 22:04:15 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
+3 -2
View File
@@ -8,6 +8,7 @@ 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 {
@@ -16,7 +17,7 @@ ext {
bintrayName = 'cicerone'
publishedGroupId = 'ru.terrakok.cicerone'
artifact = 'cicerone'
libraryVersion = '4.0.2'
libraryVersion = '5.1.1'
gitUrl = 'https://github.com/terrakok/Cicerone'
allLicenses = ['MIT']
}
@@ -24,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,6 +4,9 @@
package ru.terrakok.cicerone;
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;
@@ -27,7 +30,7 @@ public class Router extends BaseRouter {
*
* @param screen screen
*/
public void navigateTo(Screen screen) {
public void navigateTo(@NotNull Screen screen) {
executeCommands(new Forward(screen));
}
@@ -36,7 +39,7 @@ public class Router extends BaseRouter {
*
* @param screen screen
*/
public void newRootScreen(Screen screen) {
public void newRootScreen(@NotNull Screen screen) {
executeCommands(
new BackTo(null),
new Replace(screen)
@@ -51,7 +54,7 @@ public class Router extends BaseRouter {
*
* @param screen screen
*/
public void replaceScreen(Screen screen) {
public void replaceScreen(@NotNull Screen screen) {
executeCommands(new Replace(screen));
}
@@ -62,7 +65,7 @@ public class Router extends BaseRouter {
*
* @param screen screen
*/
public void backTo(Screen screen) {
public void backTo(@Nullable Screen screen) {
executeCommands(new BackTo(screen));
}
@@ -70,7 +73,7 @@ public class Router extends BaseRouter {
* Opens several screens inside single transaction.
* @param screens
*/
public void newChain(Screen... 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]);
@@ -82,7 +85,7 @@ public class Router extends BaseRouter {
* Clear current stack and open several screens inside single transaction.
* @param screens
*/
public void newRootChain(Screen... screens) {
public void newRootChain(@NotNull Screen... screens) {
Command[] commands = new Command[screens.length + 1];
commands[0] = new BackTo(null);
if (screens.length > 0) {
@@ -1,11 +1,14 @@
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;
}
@@ -6,16 +6,13 @@ 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;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.commands.Back;
import ru.terrakok.cicerone.commands.BackTo;
import ru.terrakok.cicerone.commands.Command;
import ru.terrakok.cicerone.commands.Forward;
import ru.terrakok.cicerone.commands.Replace;
/**
* Navigator implementation for launch fragments and activities.<br>
* Feature {@link BackTo} works only for fragments.<br>
@@ -23,30 +20,34 @@ import ru.terrakok.cicerone.commands.Replace;
*/
public class AppNavigator implements Navigator {
private final Activity activity;
private final FragmentManager fragmentManager;
private final int containerId;
private LinkedList<String> localStackCopy;
protected final Activity activity;
protected final FragmentManager fragmentManager;
protected final int containerId;
protected LinkedList<String> localStackCopy;
public AppNavigator(Activity activity, int containerId) {
public AppNavigator(@NotNull Activity activity, int containerId) {
this(activity, activity.getFragmentManager(), containerId);
}
public AppNavigator(Activity activity, FragmentManager fragmentManager, int containerId) {
public AppNavigator(@NotNull Activity activity, @NotNull FragmentManager fragmentManager, int containerId) {
this.activity = activity;
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommands(Command[] commands) {
public void applyCommands(@NotNull Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
applyCommand(command);
try {
applyCommand(command);
} catch (RuntimeException e) {
errorOnApplyCommand(command, e);
}
}
}
@@ -64,7 +65,7 @@ public class AppNavigator implements Navigator {
*
* @param command the navigation command to apply
*/
protected void applyCommand(Command command) {
protected void applyCommand(@NotNull Command command) {
if (command instanceof Forward) {
activityForward((Forward) command);
} else if (command instanceof Replace) {
@@ -77,7 +78,7 @@ public class AppNavigator implements Navigator {
}
protected void activityForward(Forward command) {
protected void activityForward(@NotNull Forward command) {
AppScreen screen = (AppScreen) command.getScreen();
Intent activityIntent = screen.getActivityIntent(activity);
@@ -90,7 +91,7 @@ public class AppNavigator implements Navigator {
}
}
protected void fragmentForward(Forward command) {
protected void fragmentForward(@NotNull Forward command) {
AppScreen screen = (AppScreen) command.getScreen();
Fragment fragment = createFragment(screen);
@@ -123,7 +124,7 @@ public class AppNavigator implements Navigator {
activity.finish();
}
protected void activityReplace(Replace command) {
protected void activityReplace(@NotNull Replace command) {
AppScreen screen = (AppScreen) command.getScreen();
Intent activityIntent = screen.getActivityIntent(activity);
@@ -137,7 +138,7 @@ public class AppNavigator implements Navigator {
}
}
protected void fragmentReplace(Replace command) {
protected void fragmentReplace(@NotNull Replace command) {
AppScreen screen = (AppScreen) command.getScreen();
Fragment fragment = createFragment(screen);
@@ -179,7 +180,7 @@ public class AppNavigator implements Navigator {
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
protected void backTo(@NotNull BackTo command) {
if (command.getScreen() == null) {
backToRoot();
} else {
@@ -213,10 +214,10 @@ public class AppNavigator implements Navigator {
* @param nextFragment next screen fragment
* @param fragmentTransaction fragment transaction
*/
protected void setupFragmentTransaction(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
protected void setupFragmentTransaction(@NotNull Command command,
@Nullable Fragment currentFragment,
@Nullable Fragment nextFragment,
@NotNull FragmentTransaction fragmentTransaction) {
}
/**
@@ -226,11 +227,14 @@ public class AppNavigator implements Navigator {
* @param activityIntent activity intent
* @return transition options
*/
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
@Nullable
protected Bundle createStartActivityOptions(@NotNull Command command, @NotNull Intent activityIntent) {
return null;
}
private void checkAndStartActivity(AppScreen screen, Intent activityIntent, Bundle options) {
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);
@@ -245,7 +249,7 @@ public class AppNavigator implements Navigator {
* @param screen screen
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(AppScreen screen, Intent activityIntent) {
protected void unexistingActivity(@NotNull AppScreen screen, @NotNull Intent activityIntent) {
// Do nothing by default
}
@@ -255,11 +259,13 @@ public class AppNavigator implements Navigator {
* @param screen screen
* @return instantiated fragment for the passed screen
*/
protected Fragment createFragment(AppScreen 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;
}
@@ -270,11 +276,29 @@ public class AppNavigator implements Navigator {
*
* @param screen screen
*/
protected void backToUnexisting(AppScreen screen) {
protected void backToUnexisting(@NotNull AppScreen screen) {
backToRoot();
}
protected void errorWhileCreatingScreen(AppScreen screen) {
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
/**
* 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;
}
}
@@ -4,6 +4,8 @@ import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import org.jetbrains.annotations.Nullable;
import ru.terrakok.cicerone.Screen;
/**
@@ -13,10 +15,12 @@ import ru.terrakok.cicerone.Screen;
*/
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;
}
}
@@ -3,10 +3,14 @@ package ru.terrakok.cicerone.android.support;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import 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 java.util.LinkedList;
@@ -24,30 +28,34 @@ import ru.terrakok.cicerone.commands.Replace;
*/
public class SupportAppNavigator implements Navigator {
private final Activity activity;
private final FragmentManager fragmentManager;
private final int containerId;
private LinkedList<String> localStackCopy;
protected final Activity activity;
protected final FragmentManager fragmentManager;
protected final int containerId;
protected LinkedList<String> localStackCopy;
public SupportAppNavigator(FragmentActivity activity, int containerId) {
public SupportAppNavigator(@NotNull FragmentActivity activity, int containerId) {
this(activity, activity.getSupportFragmentManager(), containerId);
}
public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) {
public SupportAppNavigator(@NotNull FragmentActivity activity, @NotNull FragmentManager fragmentManager, int containerId) {
this.activity = activity;
this.fragmentManager = fragmentManager;
this.containerId = containerId;
}
@Override
public void applyCommands(Command[] commands) {
public void applyCommands(@NotNull Command[] commands) {
fragmentManager.executePendingTransactions();
//copy stack before apply commands
copyStackToLocal();
for (Command command : commands) {
applyCommand(command);
try {
applyCommand(command);
} catch (RuntimeException e) {
errorOnApplyCommand(command, e);
}
}
}
@@ -65,7 +73,7 @@ public class SupportAppNavigator implements Navigator {
*
* @param command the navigation command to apply
*/
protected void applyCommand(Command command) {
protected void applyCommand(@NotNull Command command) {
if (command instanceof Forward) {
activityForward((Forward) command);
} else if (command instanceof Replace) {
@@ -78,7 +86,7 @@ public class SupportAppNavigator implements Navigator {
}
protected void activityForward(Forward command) {
protected void activityForward(@NotNull Forward command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
Intent activityIntent = screen.getActivityIntent(activity);
@@ -91,24 +99,13 @@ public class SupportAppNavigator implements Navigator {
}
}
protected void fragmentForward(Forward command) {
protected void fragmentForward(@NotNull Forward command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
Fragment fragment = createFragment(screen);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentParams fragmentParams = screen.getFragmentParams();
Fragment fragment = fragmentParams == null ? createFragment(screen) : null;
setupFragmentTransaction(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(screen.getScreenKey())
.commit();
localStackCopy.add(screen.getScreenKey());
forwardFragmentInternal(command, screen, fragmentParams, fragment);
}
protected void fragmentBack() {
@@ -124,7 +121,7 @@ public class SupportAppNavigator implements Navigator {
activity.finish();
}
protected void activityReplace(Replace command) {
protected void activityReplace(@NotNull Replace command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
Intent activityIntent = screen.getActivityIntent(activity);
@@ -138,28 +135,17 @@ public class SupportAppNavigator implements Navigator {
}
}
protected void fragmentReplace(Replace command) {
protected void fragmentReplace(@NotNull Replace command) {
SupportAppScreen screen = (SupportAppScreen) command.getScreen();
Fragment fragment = createFragment(screen);
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
);
fragmentTransaction
.replace(containerId, fragment)
.addToBackStack(screen.getScreenKey())
.commit();
localStackCopy.add(screen.getScreenKey());
forwardFragmentInternal(command, screen, fragmentParams, fragment);
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
@@ -171,16 +157,60 @@ public class SupportAppNavigator implements Navigator {
fragmentTransaction
);
fragmentTransaction
.replace(containerId, fragment)
.commit();
replaceFragmentInternal(fragmentTransaction, screen, fragmentParams, fragment);
fragmentTransaction.commit();
}
}
private void forwardFragmentInternal(
@NotNull Command command,
@NotNull SupportAppScreen screen,
@Nullable FragmentParams fragmentParams,
@Nullable Fragment fragment
) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
setupFragmentTransaction(
command,
fragmentManager.findFragmentById(containerId),
fragment,
fragmentTransaction
);
replaceFragmentInternal(fragmentTransaction, screen, fragmentParams, fragment);
fragmentTransaction
.addToBackStack(screen.getScreenKey())
.commit();
localStackCopy.add(screen.getScreenKey());
}
private void replaceFragmentInternal(
@NotNull FragmentTransaction transaction,
@NotNull SupportAppScreen screen,
@Nullable FragmentParams params,
@Nullable Fragment fragment
) {
if (params != null) {
transaction.replace(
containerId,
params.getFragmentClass(),
params.getArguments()
);
} else if (fragment != null) {
transaction.replace(containerId, fragment);
} else {
throw new IllegalArgumentException("Either 'params' or 'fragment' shouldn't " +
"be null for " + screen.getScreenKey());
}
}
/**
* Performs {@link BackTo} command transition
*/
protected void backTo(BackTo command) {
protected void backTo(@NotNull BackTo command) {
if (command.getScreen() == null) {
backToRoot();
} else {
@@ -214,10 +244,10 @@ public class SupportAppNavigator implements Navigator {
* @param nextFragment next screen fragment
* @param fragmentTransaction fragment transaction
*/
protected void setupFragmentTransaction(Command command,
Fragment currentFragment,
Fragment nextFragment,
FragmentTransaction fragmentTransaction) {
protected void setupFragmentTransaction(@NotNull Command command,
@Nullable Fragment currentFragment,
@Nullable Fragment nextFragment,
@NotNull FragmentTransaction fragmentTransaction) {
}
/**
@@ -227,11 +257,12 @@ public class SupportAppNavigator implements Navigator {
* @param activityIntent activity intent
* @return transition options
*/
protected Bundle createStartActivityOptions(Command command, Intent activityIntent) {
@Nullable
protected Bundle createStartActivityOptions(@NotNull Command command, @NotNull Intent activityIntent) {
return null;
}
private void checkAndStartActivity(SupportAppScreen screen, Intent activityIntent, Bundle options) {
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);
@@ -246,7 +277,7 @@ public class SupportAppNavigator implements Navigator {
* @param screen screen
* @param activityIntent intent passed to start Activity for the {@code screenKey}
*/
protected void unexistingActivity(SupportAppScreen screen, Intent activityIntent) {
protected void unexistingActivity(@NotNull SupportAppScreen screen, @NotNull Intent activityIntent) {
// Do nothing by default
}
@@ -256,11 +287,13 @@ public class SupportAppNavigator implements Navigator {
* @param screen screen
* @return instantiated fragment for the passed screen
*/
protected Fragment createFragment(SupportAppScreen 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;
}
@@ -271,11 +304,29 @@ public class SupportAppNavigator implements Navigator {
*
* @param screen screen
*/
protected void backToUnexisting(SupportAppScreen screen) {
protected void backToUnexisting(@NotNull SupportAppScreen screen) {
backToRoot();
}
protected void errorWhileCreatingScreen(SupportAppScreen screen) {
throw new RuntimeException("Can't create a screen: " + screen.getScreenKey());
/**
* 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;
}
}
@@ -2,8 +2,11 @@ package ru.terrakok.cicerone.android.support;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.Fragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import androidx.fragment.app.Fragment;
import ru.terrakok.cicerone.Screen;
/**
@@ -13,11 +16,18 @@ import ru.terrakok.cicerone.Screen;
*/
public abstract class SupportAppScreen extends Screen {
@Nullable
public Fragment getFragment() {
return null;
}
public Intent getActivityIntent(Context context) {
@Nullable
public Intent getActivityIntent(@NotNull Context context) {
return null;
}
@Nullable
public FragmentParams getFragmentParams() {
return null;
}
}
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.Nullable;
import ru.terrakok.cicerone.Navigator;
import ru.terrakok.cicerone.Screen;
@@ -20,10 +22,11 @@ public class BackTo implements Command {
*
* @param screen screen
*/
public BackTo(Screen screen) {
public BackTo(@Nullable Screen screen) {
this.screen = screen;
}
@Nullable
public Screen getScreen() {
return screen;
}
@@ -4,6 +4,8 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.NotNull;
import ru.terrakok.cicerone.Screen;
/**
@@ -17,10 +19,11 @@ public class Forward implements Command {
*
* @param screen screen
*/
public Forward(Screen screen) {
public Forward(@NotNull Screen screen) {
this.screen = screen;
}
@NotNull
public Screen getScreen() {
return screen;
}
@@ -4,12 +4,15 @@
package ru.terrakok.cicerone.commands;
import org.jetbrains.annotations.NotNull;
import ru.terrakok.cicerone.Screen;
/**
* Replaces the current screen.
*/
public class Replace implements Command {
@NotNull
private Screen screen;
/**
@@ -17,10 +20,11 @@ public class Replace implements Command {
*
* @param screen screen
*/
public Replace(Screen screen) {
public Replace(@NotNull Screen screen) {
this.screen = screen;
}
@NotNull
public Screen getScreen() {
return screen;
}
@@ -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!");
}
+5 -5
View File
@@ -5,11 +5,11 @@ repositories {
}
android {
compileSdkVersion 25
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
targetSdkVersion 25
targetSdkVersion 28
versionCode 1
versionName "1.0.0"
applicationId "ru.terrakok.cicerone.sample"
@@ -27,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"
@@ -3,7 +3,7 @@ package ru.terrakok.cicerone.sample;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.Fragment;
import androidx.fragment.app.Fragment;
import ru.terrakok.cicerone.android.support.SupportAppScreen;
import ru.terrakok.cicerone.sample.ui.animations.ProfileActivity;
@@ -1,10 +1,10 @@
package ru.terrakok.cicerone.sample.ui.animations;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import 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;
@@ -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;
@@ -26,7 +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 {
public class SelectPhotoFragment extends MvpAppCompatFragment
implements SelectPhotoView, BackButtonListener {
private static final String ARG_ANIM_DESTINATION = "arg_anim_dest";
private ImageView photo1;
@@ -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;
@@ -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.support.v4.app.FragmentTransaction;
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;
@@ -27,7 +27,8 @@ 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;
@Inject
@@ -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;
@@ -1,8 +1,8 @@
package ru.terrakok.cicerone.sample.ui.bottom;
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;
@@ -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,16 +1,17 @@
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 com.arellomobile.mvp.MvpAppCompatActivity;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import ru.terrakok.cicerone.Navigator;
@@ -28,8 +29,9 @@ import ru.terrakok.cicerone.sample.ui.common.BackButtonListener;
* on 11.10.16
*/
public class MainActivity extends MvpAppCompatActivity {
public class MainActivity extends MvpAppCompatActivity implements ChainHolder {
private TextView screensSchemeTV;
private List<WeakReference<Fragment>> chain = new ArrayList<>();
@Inject
NavigatorHolder navigatorHolder;
@@ -84,10 +86,11 @@ public class MainActivity extends MvpAppCompatActivity {
private void printScreensScheme() {
ArrayList<SampleFragment> fragments = new ArrayList<>();
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
if (fragment instanceof SampleFragment) {
fragments.add((SampleFragment) fragment);
}
for (WeakReference<Fragment> fragmentReference : chain) {
Fragment fragment = fragmentReference.get();
if (fragment != null && fragment instanceof SampleFragment) {
fragments.add((SampleFragment) fragment);
}
}
Collections.sort(fragments, new Comparator<SampleFragment>() {
@Override
@@ -105,4 +108,9 @@ public class MainActivity extends MvpAppCompatActivity {
}
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,7 +23,7 @@ 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";
@@ -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"/>
@@ -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"