Giter Site home page Giter Site logo

terrakok / cicerone Goto Github PK

View Code? Open in Web Editor NEW
2.6K 53.0 214.0 5.32 MB

🚦 Cicerone is a lightweight library that makes the navigation in an Android app easy.

License: Other

Java 15.77% Kotlin 84.23%
navigation android fragments jetpack-navigation cicerone multistack bottom-navigation

cicerone's Introduction

Cicerone

Maven Central License: MIT

Android Arsenal Android Weekly Android Weekly

Power navigation Multibackstack Result listeners

Cicerone (a guide who gives information about antiquities and places of interest to sightseers) is a lightweight library that makes the navigation in an Android app easy.
It was designed to be used with the MVP/MVVM/MVI patterns but will work great with any architecture.

Main advantages

  • Is not tied to Fragments
  • Not a framework (very lightweight)
  • Short navigation calls (no builders)
  • Static typed checks for screen parameters!
  • Lifecycle-safe!
  • Functionality is simple to extend
  • Suitable for Unit Testing

Additional features

  • Opening several screens inside single call (for example: deeplink)
  • Provides FragmentFactory if it needed
  • add or replace strategy for opening next screen (see router.navigateTo last parameter)
  • Implementation of parallel navigation (Instagram like)
  • Predefined navigator ready for Single-Activity apps
  • Predefined navigator ready for setup transition animation

How to add Cicerone to your application

Add the dependency in your build.gradle:

dependencies {
    //Cicerone
    implementation("com.github.terrakok:cicerone:X.X.X")
}

Initialize the library (for example in your Application class):

class App : Application() {
    private val cicerone = Cicerone.create()
    val router get() = cicerone.router
    val navigatorHolder get() = cicerone.getNavigatorHolder()

    override fun onCreate() {
        super.onCreate()
        INSTANCE = this
    }

    companion object {
        internal lateinit var INSTANCE: App
            private set
    }
}

How does it work?

CiceroneDiagram.png

The Presenter calls the navigation method of Router.

class SamplePresenter(
    private val router: Router
) : Presenter<SampleView>() {

    fun onOpenNewScreen() {
        router.navigateTo(SomeScreen())
    }

    fun onBackPressed() {
        router.exit()
    }
}

Router converts the navigation call to the set of commands and sends them to CommandBuffer.

CommandBuffer checks whether there are the _"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 a new _"active"_ Navigator will appear.
fun executeCommands(commands: Array<out Command>) {
    navigator?.applyCommands(commands) ?: pendingCommands.add(commands)
}

Navigator processes the navigation commands. Usually it is an anonymous class inside Activity. Activity provides Navigator to the CommandBuffer in _onResume_ and removes it in _onPause_.

Attention: Use _onResumeFragments()_ with FragmentActivity (more info)

private val navigator = AppNavigator(this, R.id.container)

override fun onResumeFragments() {
    super.onResumeFragments()
    navigatorHolder.setNavigator(navigator)
}

override fun onPause() {
    navigatorHolder.removeNavigator()
    super.onPause()
}

Navigation commands

These commands will fulfill the needs of the most applications. But if you need something special - just add it!

  • Forward - Opens new screen
  • Back - Rolls back the last transition
  • BackTo - Rolls back to the needed screen in the screens chain
  • Replace - Replaces the current screen

Predefined navigator

The library provides predefined navigator for Fragments and Activity. To use, just provide it with the container and FragmentManager.

private val navigator = AppNavigator(this, R.id.container)

A custom navigator can be useful sometimes:

private val navigator = object : AppNavigator(this, R.id.container) {
    override fun setupFragmentTransaction(
        screen: FragmentScreen,
        fragmentTransaction: FragmentTransaction,
        currentFragment: Fragment?,
        nextFragment: Fragment
    ) {
        //setup your animation
    }

    override fun applyCommands(commands: Array<out Command>) {
        hideKeyboard()
        super.applyCommands(commands)
    }
}

Screens

Describe your screens as you like e.g. create a Kotlin object with all application screens:

object Screens {
    fun Main() = FragmentScreen { MainFragment() }
    fun AddressSearch() = FragmentScreen { AddressSearchFragment() }
    fun Profile(userId: Long) = FragmentScreen("Profile_$userId") { ProfileFragment(userId) }
    fun Browser(url: String) = ActivityScreen { Intent(Intent.ACTION_VIEW, Uri.parse(url))  }
}

Additional you can use FragmentFactory for creating your screens:

fun SomeScreen() = FragmentScreen { factory: FragmentFactory -> ... }

Screen parameters and result listener

//you have to specify screen parameters via new FragmentScreen creation
fun SelectPhoto(resultKey: String) = FragmentScreen {
    SelectPhotoFragment.getNewInstance(resultKey)
}
//listen result
fun onSelectPhotoClicked() {
    router.setResultListener(RESULT_KEY) { data ->
        view.showPhoto(data as Bitmap)
    }
    router.navigateTo(SelectPhoto(RESULT_KEY))
}

//send result
fun onPhotoClick(photo: Bitmap) {
    router.sendResult(resultKey, photoRes)
    router.exit()
}

Sample

To see how to add, initialize and use the library and predefined navigators see the sample project
(thank you @Javernaut for support new library version and migrate sample project to Kotlin!)

For more complex use case check out the GitFox (Android GitLab client)

Applications that use Cicerone

Яндекс.Еда — доставка еды/продуктов. Food delivery
Kaspersky Internet Security
Delivery Club – Доставка еды и продуктов
Поиск работы на hh. Вакансии рядом с домом
Whisk: Recipe Saver, Meal Planner & Grocery List
Мой Beeline (Казахстан)
Mercuryo Bitcoin Cryptowallet
ЧекСкан - кэшбэк за чеки, цены и акции в магазинах
RSS Reader для Вести.Ru
EPAM Connect
Consumer Reports: Product Reviews & Ratings
Zakaz.ru

Participants

  • Idea and code - Konstantin Tskhovrebov (@terrakok)
  • Architecture advice, documentation and publication - Vasili Chyrvon (@Jeevuz)

License

MIT License

Copyright (c) 2017 Konstantin Tskhovrebov (@terrakok)
                   and Vasili Chyrvon (@Jeevuz)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

cicerone's People

Contributors

aasitnikov avatar adolgiy avatar adpops avatar andreipapko avatar anton111111 avatar dmitrynerd avatar eduard1abdulmanov123 avatar electrolobzik avatar javernaut avatar jeevuz avatar laomo avatar phansier avatar pihariev avatar popalay avatar qwert2603 avatar sitnikovimprove avatar terrakok avatar vitalyperyatin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cicerone's Issues

How to transfer data with a command exit()

Hello! I need to transfer the data to the previous activity screen, because the current screen was launched with the command onActivityScreen. How can i do this? If for example the command navigateTo allows me to do this, then exit() not have an object parameter for data transfer. Thanks in advance for the answer.

Can not perform this action after onSaveInstanceState in setNavigator

Activity's OnResume and OnPause looks exactly like this

@Override
protected void onResume(){
    super.onResume();
    MyApp.INSTANCE.getNavigatorHolder().setNavigator(navigator);
}

@Override
protected void onPause(){
    MyApp.INSTANCE.getNavigatorHolder().removeNavigator();
    super.onPause();
}

This exception is reported by remote device and I really don't know how reproduce it, so the only information I have is the following stack trace:

Fatal Exception: java.lang.RuntimeException: Unable to resume activity {my.app/my.app.activity.RootActivity}: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3226)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3257)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1514)
    at android.os.Handler.dispatchMessage(Handler.java:111)
    at android.os.Looper.loop(Looper.java:194)
    at android.app.ActivityThread.main(ActivityThread.java:5631)
    at java.lang.reflect.Method.invoke(Method.java)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
Caused by java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1842)
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1860)
    at android.support.v4.app.FragmentManagerImpl.popBackStack(FragmentManager.java:770)
    at ru.terrakok.cicerone.android.SupportFragmentNavigator.backToRoot(SupportFragmentNavigator.java:145)
    at ru.terrakok.cicerone.android.SupportFragmentNavigator.backToUnexisting(SupportFragmentNavigator.java:189)
    at ru.terrakok.cicerone.android.SupportFragmentNavigator.applyCommand(SupportFragmentNavigator.java:135)
    at ru.terrakok.cicerone.CommandBuffer.executeCommand(CommandBuffer.java:43)
    at ru.terrakok.cicerone.CommandBuffer.setNavigator(CommandBuffer.java:26)
    at my.app.activity.RootActivity.onResume(RootActivity.java:1248)
    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1267)
    at android.app.Activity.performResume(Activity.java:6178)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3211)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3257)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1514)
    at android.os.Handler.dispatchMessage(Handler.java:111)
    at android.os.Looper.loop(Looper.java:194)
    at android.app.ActivityThread.main(ActivityThread.java:5631)
    at java.lang.reflect.Method.invoke(Method.java)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)

I have two different devices which was affected by this issue:

Philips V377 Android 5.1
UMI X2 Android 4.2.1

Support for androidX packages

This library would need to update references:
android.support.v4.app.Fragment to androidx.fragment.app.Fragment
android.support.v4.app.FragmentActivity to androidx.fragment.app.FragmentActivity

among others, to support migrations to androidX packages.

Edit: To be clear, it does currently work with jetifier, but throws up error-level warnings in studio, which always has me checking back to classes to make sure I haven't messed something up.

Maybe we could do another artifact for this, for the sake of compatibility for those who haven't yet updated?

Back button clicks

I have an idea. How do you think - is it ok or not?

If we delegate Activity#onBackPressed() to something Cicerone has access and then notify about it everything local stack copy has, so we can remove this ugly nullable current fragment reference in activity.

Think this will also be good because of synchronized and only-available-fragment nature of navigator.

More documentation please!

Hi, I've just started using this library and am finding it hard to see what the implementations of Command, Navigator, etc are. There's no API spec that I can find short of crawling through the source myself.

A javadoc would be helpful. Thanks!

IllegalArgumentException while using Kotlin and pass only one parameter to router.navigateTo()

If I use router.navigateTo() without passing data as a second parameter like this:
App.INSTANCE.router.navigateTo(SCREEN_NAME)
the app crashes with IllegalArgumentException. Part of stacktrace:

Process: com.example.etmkotlin, PID: 4753
    java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter data
        at com.example.etmkotlin.activities.MainActivity$navigator$1.createFragment(MainActivity.kt)
        at ru.terrakok.cicerone.android.SupportFragmentNavigator.forward(SupportFragmentNavigator.java:107)
        at ru.terrakok.cicerone.android.SupportFragmentNavigator.applyCommand(SupportFragmentNavigator.java:91)
        at ru.terrakok.cicerone.android.SupportFragmentNavigator.applyCommands(SupportFragmentNavigator.java:71)
        at ru.terrakok.cicerone.CommandBuffer.executeCommands(CommandBuffer.java:42)
        at ru.terrakok.cicerone.BaseRouter.executeCommands(BaseRouter.java:30)
        at ru.terrakok.cicerone.Router.navigateTo(Router.java:83)
        at ru.terrakok.cicerone.Router.navigateTo(Router.java:73)
        at com.example.etmkotlin.activities.MainActivity$override.openScreen(MainActivity.kt:107)
...

Part of Router.java:

/**
     * Open new screen and add it to the screens chain.
     *
     * @param screenKey screen key
     */
    public void navigateTo(String screenKey) {
        navigateTo(screenKey, null);
    }

    /**
     * Open new screen and add it to screens chain.
     *
     * @param screenKey screen key
     * @param data      initialisation parameters for the new screen
     */
    public void navigateTo(String screenKey, Object data) {
        executeCommands(new Forward(screenKey, data));
    }

So if the data is null it will crash.
But if I pass some data for example:
App.INSTANCE.router.navigateTo(SCREEN_NAME, "123")
everything wotks great.

MinSdk

Cicerone min sdk is 19, like in sample app, or previous APIs also compatible?
Also how do you use showSystemMessage for everything else except toasts? Overriding?

Check if container already has this fragment

I got a question. My Navigation class contains Router. There is no activity context. And for example my ViewModel doesn't know what fragment contains in container right now. It try to set fragment already contains and it replace but new Fragment("Fragment A" replace/forward/etc "Fragment A"). How can I prevent this and do nothing in this case?

router injection question

Is it right to inject router in presenter with dagger and moxy? Or more correct way is injecting in fragment and providing to presenter?
What the difference?

ResultListener not setting to router.

Trying to set result listener using this code:

INSTANCE.cicerone.navigatorHolder.setNavigator((activity as MainActivity).navigator) getRouter().setResultListener(Utils.NEW_PROJECT_RESULT_CODE, newProjectResultListener) getRouter().navigateTo(FragmentIdentifiers.DETAILED_PROJECT_FRAGMENT)
but when I trying to use exitWithResult nothing is happens cause the set of result listeners in empty.

How to fix it?

Methods showSystemMessage() and exit() not work in Executor

The code sample-cicerone, and change this:

public void onForwardWithDelayCommandClick() {
        if (future != null) future.cancel(true);
        future = executorService.schedule(new Runnable() {
            @Override
            public void run() {
             router.showSystemMessage("CHECK");
             router.exit();
             //router.navigateTo(Screens.SAMPLE_SCREEN + (screenNumber + 1), screenNumber + 1);
            }
        }, 5, TimeUnit.SECONDS);
    }

Nothing will happen! The methods of the "router" are executed, but no action will occur. And the "navigateTo" method will be executed!

Why do not "exit" and "showSystemMessage" work?

Child fragments

Do you have any examples of working examples with child inner fragments?

The best way to pass arguments to a fragment.

So, guys, you have:

private Navigator navigator = new SupportFragmentNavigator(getSupportFragmentManager(), R.id.main_container) {
        @Override
        protected Fragment createFragment(String screenKey, Object data) {
            return SampleFragment.getNewInstance((int) data);
        }
}
public static SampleFragment getNewInstance(int number) {
        SampleFragment fragment = new SampleFragment();

        Bundle args = new Bundle();
        args.putInt(EXTRA_NUMBER, number);
        fragment.setArguments(args);

        return fragment;
    }

and then you execute it with a pretty simple command:

navigator.applyCommand(new Replace(Screens.SAMPLE_SCREEN, 1));

But what if I have more than argument?
Lets say 5 of them?
I can create a helper class with this 5 fields, or pass bundle every time I need to open a fragment.
Maybe you have a better way?

Back Stack

I have activity A that opens Fragment A1 and then Fragment A2. And when i want to navigate to activity B, how can i remove fragment A2 from back stack without screen blinking , so when i back from activity B to activity A there would be only fragment A1 ?

Get current fragment

Hello,

Is there a listener/callback which can be attached to Cicerone, which would trigger when there was a screen change?

If not, is there a method which will return the current visible screen, or the backstack?

How to transist complex data between views?

I've seen in your library that you pass data between views with Object data parametr? Could you give me advice how to pass complex data? For example, I want to pass one Integer and one String, what I need to do?

sendResult without exit

Please, implement sendResult without exit.
Smth like: router.sendResult(code, object) (now this method is protected 🌵)

Fragment transaction animation issue with nested navigation implemented

Thanks for the great lib!
The usecase:
I've got an app with nested navigation like in your example app. Got several instances of the TabContainerFragment wich could be selected by tapping on bottom navigation bar's icons.
I overrided setupFragmentTransactionAnimation method in TabContainerFragment's navigator and described transition from fragment A to fragment B with animation xml files.
Steps:
Let's say in tab 1 I move forward from fragment A to fragment B. Animation works perfectly. After that I select tab 2 that contains fragment C and return back to tab 1, that has fragment B. At this point fragment B appears with the last applied animation again. But I don't want to see this animation when I navigating between the tabs. How can I disable it?

Thanks in advance!

Replace transition doesn't work for activities

In my Android application I'm writing splash activity screen with makes transition to the core bottom navigation activty with one second delay. In my transition I want to replace splash screen and remove it from backstack.

For this purpose I wrote such code:
https://gist.github.com/LAHomieJob/aa81ae68309d73e2fa73319c1b46c5d5

My problem is that router.replaceScreen behaves like Router method navigateTo - SplashActivity remains inside in back stack and I can reach it via back button

What's wrong with my code?

Add fragments instead of replacing them

I'm wondering if there is a way to alter the FragmentTransaction instance, which is being used in the forward(Forward command) method, as seen in the SupportFragmentNavigator class. I need to fragmentTransaction.add() fragments instead of fragmentTransaction.replace()-ing them.

I'm using the navigateTo method to show the next Fragment, like so:

App.get().getRouter().navigateTo(Screens.CUSTOMER_DETAIL, customer);

I have a Fragment which basically hosts a RecyclerView with a lots of data (customers), returned by remote API.
Clicking an item from that list will open a new screen which will host detailed data for that customer.

A common scenario you might guess.

When I go back from the detail screen, the API is invoked again, the list reloaded, scrolling position gone, search filters invalidated etc.
That's because the library uses fragmentTransaction.replace(), but fragmentTransaction.add() is what I need, to avoid the reload.

I've copied locally these two classes: SupportFragmentNavigator and SupportAppNavigator and changed what I need, and it works.
But it's a dirty solution.

Is there an API in Cicerone which will provide customization options for this manner?

Add hint for using onResumeFragments instead of onResume for FragmentActivity

As said in https://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume():

Note that for better inter-operation with older versions of the platform, at the point of this call the fragments attached to the activity are not resumed. This means that in some cases the previous state may still be saved, not allowing fragment transactions that modify the state. To correctly interact with fragments in their proper state, you should instead override onResumeFragments().

Two navigators in one activity

Hi again. I have a BaseActivity and I want to put both fragment and Activity navigators in it, because I want to open fragment/activity from any place/screen of my app. Is this possible?
Suppose I want to navigate to fragment or to other activity from currentActivity.

SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(fragment);
SampleApplication.INSTANCE.getNavigatorHolder().setNavigator(activity); // addNavigator()?

Return data on router.backTo(...)

router.navigateTo(EXTRA_NEXT_SCREEN, new Object()) is a great feature and I'm using it in my projects, but sometimes I need to return some data to previous step/fragment, for example, when I need to return token after phone confirm.
It would be great if I can use something like router.backTo(EXTRA_PREVIOUS_SCREEN, new Object()) inside Cicerone routers logic and without necessity of injecting additional interactors to presenters.

Open activity from fragment

How i can open new activity from fragment? I use SupportFragmentNavigator which adding to router in activity.

Make 'Navigator.applyCommand()' return boolean to indicate if command was recognized

I believe this would make a better API in terms of composing several navigators.

It would be more natural to do something like this:

class TopNavigator(private val childNavigator: Navigator) : Navigator {
  fun applyCommand(command: Command): Boolean {
    if (childNavigator.applyCommand(command)) {
        // child has handled this command, great!
    } else {
       handleCommandInTopNavigator()
    }
}

With the current implementation returning "void" it is not simple to write above logic, you'd have to resort to some "hacky" ways.

If you are not against this suggestion, I could prepare a PR.

How to make transition from one activity to another with Parcelable object?

In my app I make transitions between activities with the help of Cicerone library. The code of my activity is https://gist.github.com/LAHomieJob/d786355666795a8f58b8e7975d302e49#file-choosecallactivity-kt-L152
I want to pass intent with "phoneCallRepresentation" parcelable object, which will be known only during runtime (it depends on selection from recyclerView). I don't know how to do it. Do I need to write putExtra to intent? Or I need to pass this object only in router.navigateTo() as a parameter?

Example was broken

The example was broken after last commits in the library.
In MainActivity and TabContainerFragment there is constructor error in SupportFragmentNavigator.

Use Cicerone without Moxy

Readme says: "It (Cicerone) was designed to be used with the MVP pattern (try Moxy), but will work great with any architecture."

Is there any tutorial how to use Cicerone without an MVP architecture? Even the Cicerone library includes Moxy as a dependency. Is there a way to make Cicerone a bit more loosely coupled?

Rename Router.exit()

I think we need to rename Router.exit() method. It constantly causes misunderstanding from new people.

My proposal is back() as most intuitive one.

Using popBackStackImmediate() to replace the fragment

Hi, thanks for the library!

Now I'm trying to use it in my current project, and I've faced the UI 'blink' artifact while replacing the fragments using the SupportFragmentNavigator class provided by the library. So, the issue is:

  1. navigate to the fragment A. The keyboard should appear here, so the appropriate code is added in the onViewCreated method.
  2. navigate to the fragment B (with the navigateTo method).
  3. navigate to the fragment C. When the back button is pressed, A fragment should be shown, and U use the replaceScreen method

The blink happens because onViewCreated method is called while replacing B with C, and the keyboard tries to be shown for a moment.

Do you have any recommendations how to handle this situation?

SupportFragmentNavigator.BackToRoot only pops one fragment off the backstack

SupportFragmentNavigator

    private void backToRoot() {
        fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        localStackCopy.clear();
    }

Shouldn't this pop all fragments off the backstack until we reach the root? Currently, it only pops the top one.

As a result, the following navigation happens:
On: A
Navigate: Forward to B
On: B
Navigate: newScreenChain starting with A
On: A
Navigate: Back
On: B <--- what?! But we called newScreenChain!

setupFragmentTransactionAnimation is not called in child fragment

First of all, nice library. It's been helping me a lot.
I'm having a problem in a setup similar to the BottomNavigationActivity of the sample.
I want to have a slide animation when navigating inside a tab (opening forwardFragments) and no animation or some other animation when I switch tabs. The problem is: when I switch tabs, the parent fragment recreates the child and this forces the animation that was previously set up, without even calling setupFragmentTransactionAnimation() again, to give me a chance to disable or change the animation. This is my guess of what's happening, at least.
To reproduce this, simply add any animation to the TabContainerFragment's Navigator. Then navigate inside a tab, and then between tabs.

Am I misusing the library, or is this a bug ? Could we have a method that is always called when a transaction will happen ?

Thanks !

ArrayIndexOutOfBoundsException

/**
    * Clear current stack and open several screens inside single transaction.
    * @param screens
    */
   public void newRootChain(Screen... screens) {
       Command[] commands = new Command[screens.length + 1];
       commands[0] = new BackTo(null);
       for (int i = 0; i < commands.length; i++) {
           commands[i + 1] = new Forward(screens[i]);
       }
       executeCommands(commands);
   }

This function always results in ArrayIndexOutOfBoundsException because of iteration comparison with commands.lenght, and it should compare to screens.lenght

java.lang.IllegalStateException: ... declared target fragment FilterFragment{9cee0b7} that does not belong to this FragmentManager!

I've got an exception when trying set Target Fragment (Android 7.0, Samsung J-330), support version - 27.1.0

BRAND_SELECTION_FRAGMENT -> {
                    val fragment = BrandSelectionFragment.newInstance(data as ArrayList<StationNetwork>)
                    fragment.setTargetFragment(filterFragment, BRAND_SELECTION_REQUEST_CODE)
                    fragment
                }
java.lang.IllegalStateException: Fragment BrandSelectionFragment{99d4bb6 #1 id=0x7f0a00c8} declared target fragment FilterFragment{9cee0b7} that does not belong to this FragmentManager!
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1354)
        at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1188)
        at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:1071)
        at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:115)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2379)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2337)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2244)
        at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:702)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6776)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)

is this still Android bug and what could be the steps to fix it?

Activity navigation without supporting Fragments

Hi,
As it stands, SupportAppNavigator forces support for both Activity and Fragments when implementing it. If I'm refraining from using fragments altogether, then it doesn't make as much sense for me to put in a containerId(which needs a primitive int value) and to override createFragment and pass back an arbitrary non-null Fragment.

While it is easy enough to override the Navigator interface and build my own Activity-only navigator, it would be nice if we could have one in the library ready to use, out-of-the-box. Could contribute this myself too. Thoughts?

Get 'transitionData' on new Activity

I happen to have the following line in my code, which invokes an Activity and passing a Map as argument:
router?.navigateTo(RssFeedActivity.TAG, dataMap)

My question is how to retrieve that data once the new Activity is running (similar to what, for instance, getIntent().geStringExtra("myStringKey") does when simply using "plain" Android).

Make activity class variable accessible for AppNavigator's subclasses

I want to implement my navigator in separate class to make Activity's code look clearer. I need activity instance for createActivityIntent method. But activity class variable in AppNavigator is private. So i think it would be better to make it protected because now I have to declare another activity variable for my class and it looks really bad.

Check current screen is root.

Hi!
Thank you for good tool.
It would be so good to add "to the box" function to check, if current screen is root.
At now I'm extending Support(App|Fragment)Navigator with function "isRootScreen". I think, it is useful function.

Passing result through multiple activities

Hi,

Would this lib allow a result to be passed across multiple activities like :
A need result
D produce result
But B is finished or destroyed by android

A -> B (destroyed) -> C -> D

maybe a command like
BackToWithResult(screen, result)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.