Giter VIP home page Giter VIP logo

simple-stack's Introduction

featured License

simple-stack

Simple Stack

Why do I want this?

To make navigation to another screen as simple as backstack.goTo(SomeScreen()), and going back as simple as backstack.goBack().

No more FragmentTransactions in random places. Predictable and customizable navigation in a single location.

What is Simple Stack?

Simple Stack is a backstack library (or technically, a navigation framework) that allows you to represent your navigation state in a list of immutable, parcelable data classes ("keys").

This allows preserving your navigation history across configuration changes and process death - this is handled automatically.

Each screen can be associated with a scope, or a shared scope - to easily share data between screens.

This simplifies navigation and state management within an Activity using either fragments, views, or whatever else.

Using Simple Stack

In order to use Simple Stack, you need to add jitpack to your project root build.gradle.kts (or build.gradle):

// build.gradle.kts
allprojects {
    repositories {
        // ...
        maven { setUrl("https://jitpack.io") }
    }
    // ...
}

or

// build.gradle
allprojects {
    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
    // ...
}

In newer projects, you need to also update the settings.gradle file's dependencyResolutionManagement block:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }  // <--
        jcenter() // Warning: this repository is going to shut down soon
    }
}

and then, add the dependency to your module's build.gradle.kts (or build.gradle):

// build.gradle.kts
implementation("com.github.Zhuinden:simple-stack:2.9.0")
implementation("com.github.Zhuinden:simple-stack-extensions:2.3.4")

or

// build.gradle
implementation 'com.github.Zhuinden:simple-stack:2.9.0'
implementation 'com.github.Zhuinden:simple-stack-extensions:2.3.4'

How do I use it?

You can check out the tutorials for simple examples.

Fragments

With Fragments, in AHEAD_OF_TIME back handling mode to support predictive back gesture (along with android:enableBackInvokedCallback), the Activity code looks like this:

  • With simple-stack-extensions:lifecycle-ktx
class MainActivity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
    private lateinit var fragmentStateChanger: FragmentStateChanger
    private lateinit var backstack: Backstack

    private val backPressedCallback = object : OnBackPressedCallback(false) { // <-- !
        override fun handleOnBackPressed() {
            backstack.goBack()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.main_activity)

        onBackPressedDispatcher.addCallback(backPressedCallback) // <-- !

        fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.container)

        backstack = Navigator.configure()
            .setBackHandlingModel(BackHandlingModel.AHEAD_OF_TIME) // <-- !
            .setStateChanger(SimpleStateChanger(this))
            .install(this, binding.container, History.single(HomeKey))

        backPressedCallback.isEnabled = backstack.willHandleAheadOfTimeBack() // <-- !
        backstack.observeAheadOfTimeWillHandleBackChanged(this, backPressedCallback::isEnabled::set) // <-- ! from lifecycle-ktx
    }
    
    override fun onNavigationEvent(stateChange: StateChange) {
        fragmentStateChanger.handleStateChange(stateChange)
    }
}
  • Without simple-stack-extensions:lifecycle-ktx
class MainActivity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
    private lateinit var fragmentStateChanger: FragmentStateChanger
    private lateinit var backstack: Backstack

    private val backPressedCallback = object : OnBackPressedCallback(false) { // <-- !
        override fun handleOnBackPressed() {
            backstack.goBack()
        }
    }

    private val updateBackPressedCallback = AheadOfTimeWillHandleBackChangedListener { // <-- !
        backPressedCallback.isEnabled = it // <-- !
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.main_activity)

        onBackPressedDispatcher.addCallback(backPressedCallback) // <-- !

        fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.container)

        backstack = Navigator.configure()
            .setBackHandlingModel(BackHandlingModel.AHEAD_OF_TIME) // <-- !
            .setStateChanger(SimpleStateChanger(this))
            .install(this, binding.container, History.single(HomeKey))

        backPressedCallback.isEnabled = backstack.willHandleAheadOfTimeBack() // <-- !
        backstack.addAheadOfTimeWillHandleBackChangedListener(updateBackPressedCallback) // <-- !
    }

    override fun onDestroy() {
        backstack.removeAheadOfTimeWillHandleBackChangedListener(updateBackPressedCallback); // <-- !
        super.onDestroy()
    }

    override fun onNavigationEvent(stateChange: StateChange) {
        fragmentStateChanger.handleStateChange(stateChange)
    }
}

With targetSdkVersion 34 and with android:enableOnBackInvokedCallback="true" enabled, onBackPressed ( and KEYCODE_BACK) will no longer be called. In that case, the AHEAD_OF_TIME back handling model should be preferred.

Screens

FirstScreen looks like this (assuming you have data object enabled):

kotlinOptions {
    jvmTarget = "1.8"
    languageVersion = '1.9' // data objects, 1.8 in 1.7.21, 1.9 in 1.8.10
}

kotlin.sourceSets.all {
    languageSettings.enableLanguageFeature("DataObjects")
}
// no args
@Parcelize
data object FirstScreen : DefaultFragmentKey() {
    override fun instantiateFragment(): Fragment = FirstFragment()
}

If you don't have data object support yet, then no-args keys look like this (to ensure stable hashCode/equals/toString):

// no args
@Parcelize
data class FirstScreen(private val noArgsPlaceholder: String = ""): DefaultFragmentKey() {
    override fun instantiateFragment(): Fragment = FirstFragment()
}

// has args
@Parcelize
data class FirstScreen(
    val username: String, 
    val password: String,
): DefaultFragmentKey() {
    override fun instantiateFragment(): Fragment = FirstFragment()
}

And FirstFragment looks like this:

class FirstFragment: KeyedFragment(R.layout.first_fragment) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val key: FirstScreen = getKey() // params
    }
}

After which going to the second screen is as simple as backstack.goTo(SecondScreen()).

Scopes

To simplify sharing data/state between screens, a screen key can implement ScopeKey.

The scope is described with a String tag, and services bound to that scope can be configured via ScopedServices.

Services bound to a ServiceBinder get lifecycle callbacks: ScopedServices.Registered, ScopedServices.Activated, or Bundleable.

This lets you easily share a class between screens, while still letting you handle Android's lifecycles seamlessly.

Using the simple-stack-extensions, this can be simplified using the DefaultServiceProvider.

It looks like this:

Navigator.configure()
    .setScopedServices(DefaultServiceProvider())
    /* ... */

And then:

@Parcelize // typically data class
data object FirstScreen: DefaultFragmentKey(), DefaultServiceProvider.HasServices {
    override fun instantiateFragment(): Fragment = FirstFragment()

    override fun getScopeTag() = toString()

    override fun bindServices(serviceBinder: ServiceBinder) {
        with(serviceBinder) {
            add(FirstScopedModel())
        }
    }
}

class FirstScopedModel : Bundleable, ScopedServices.Registered { // interfaces are optional
    ...
}

class FirstFragment : KeyedFragment(R.layout.first_fragment) {
    private val firstModel by lazy { lookup<FirstScopedModel>() }

    ...
}

class SecondFragment : KeyedFragment(R.layout.second_fragment) {
    private val firstModel by lazy { lookup<FirstScopedModel>() } // <- available if FirstScreen is in the backstack

    ...
}

And FirstScopedModel is shared between two screens.

Any additional shared scopes on top of screen scopes can be defined using ScopeKey.Child.

What are additional benefits?

Making your navigation state explicit means you're in control of your application.

Instead of hacking around with the right fragment transaction tags, or calling NEW_TASK | CLEAR_TASK and making the screen flicker - you can just say backstack.setHistory(History.of(SomeScreen(), OtherScreen()) and that is now your active navigation history.

Using Backstack to navigate allows you to move navigation responsibilities out of your view layer. No need to run FragmentTransactions directly in a click listener each time you want to move to a different screen. No need to mess around with LiveData<Event<T>> or SingleLiveData to get your "view" to decide what state your app should be in either.

class FirstScopedModel(private val backstack: Backstack) {
    fun doSomething() {
        // ...
        backstack.goTo(SecondScreen)
    }
}

Another additional benefit is that your navigation history can be unit tested.

assertThat(backstack.getHistory()).containsExactly(SomeScreen, OtherScreen)

And most importantly, navigation (swapping screens) happens in one place, and you are in direct control of what happens in such a scenario. By writing a StateChanger, you can set up "how to display my current navigation state" in any way you want. No more ((MainActivity)getActivity()).setTitleText("blah"); inside Fragment's onStart().

Write once, works in all cases.

override fun onNavigationEvent(stateChange: StateChange) { // using SimpleStateChanger
    val newScreen = stateChange.topNewKey<MyScreen>() // use your new navigation state

    setTitle(newScreen.title);

    ... // set up fragments, set up views, whatever you want
}

Whether you navigate forward or backward, or you rotate the screen, or you come back after low memory condition - it's irrelevant. The StateChanger will always handle the scenario in a predictable way.

Dev Talk about Simple-Stack

For an overview of the "why" and the "what" of what Simple-Stack offers, you can check out this talk called Simplified Single-Activity Apps using Simple-Stack .

Tutorial by Ryan Kay

For a quick tutorial on how to set up dependency injection, model lifecycles, and reactive state management using Simple-Stack, you can look at the tutorial by Ryan Michael Kay here, by clicking this link.)

More information

For more information, check the wiki page.

What about Jetpack Compose?

See https://github.com/Zhuinden/simple-stack-compose-integration/ for a default way to use composables as screens.

This however is only required if ONLY composables are used, and NO fragments. When using Fragments, refer to the official Fragment Compose interop guide.

For Fragment + Simple-Stack + Compose integration, you can also check the corresponding sample .

About the event-bubbling back handling model

This section is provided for those who are transitioning from event-bubbling to the ahead-of-time back handling model (OnBackPressedDispatcher), but cannot use the ahead-of-time model yet (due to relying on onBackPressed() or KEYCODE_BACK).

Note: Before supporting predictive back gestures and using EVENT_BUBBLING back handling model, the code that interops with OnBackPressedDispatcher looks like this:

class MainActivity : AppCompatActivity(), SimpleStateChanger.NavigationHandler {
    private lateinit var fragmentStateChanger: DefaultFragmentStateChanger

    @Suppress("DEPRECATION")
    private val backPressedCallback = object: OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if (!Navigator.onBackPressed(this@MainActivity)) {
                this.remove() 
                onBackPressed() // this is the reliable way to handle back for now 
                this@MainActivity.onBackPressedDispatcher.addCallback(this)
            }
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        onBackPressedDispatcher.addCallback(backPressedCallback) // this is the reliable way to handle back for now

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        fragmentStateChanger = DefaultFragmentStateChanger(supportFragmentManager, R.id.container)
        
        Navigator.configure()
            .setStateChanger(SimpleStateChanger(this))
            .install(this, binding.container, History.single(HomeKey))
    }

    override fun onNavigationEvent(stateChange: StateChange) {
        fragmentStateChanger.handleStateChange(stateChange)
    }
}

To handle back previously, what you had to do is override onBackPressed() (then call backstack.goBack(), if it returned true then you would not call super.onBackPressed()) , but in order to support BackHandler in Compose, or Fragments that use OnBackPressedDispatcher internally, you cannot override onBackPressed anymore in a reliable manner.

Now, either this should be used (if cannot migrate to AHEAD_OF_TIME back handling model), or migrate to AHEAD_OF_TIME back handling model and AheadOfTimeBackCallback (see example at the start of this readme).

License

Copyright 2017-2023 Gabor Varadi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

simple-stack's People

Contributors

fornewid avatar ninjahoahong avatar rbaumert avatar zhuinden 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

simple-stack's Issues

Allow possibility of "not saving a given history element"

Flow added a HistoryFilter. While I've never personally had a need for this, apparently there can be requirements where "restore where you were in the list, but don't restore the detail page"; therefore a way to filter that out from persistence might be a good idea.

please annotate method parameters with Nullable and NonNull

Hello, I'm giving a try at Simple-stack (a bit pissed of by the slow pace of development of flow and also
hoping that it has better state management...)

Could you please annotate paramethers in methods, like in LayoutInflationStrategy ?
When using Simple-stack from kotlin, lots of things look nullable when they shouldn't be.

Recipes

I'm currently migrating a complex app from Mortar+Flow to Mortar+SimpleStack

I have a key that leads to different screens (a search screen, an introscreen, a progress screen that waits when a database is ready to go to the search screen, etc...) doing some kind of redirection.

I was hoping to do it with simplestack but

  1. building my data and when it is ready, calling

Navigator.getBackstack(this).setHistory(HistoryBuilder.from(Navigator.getBackstack(this)).build(), StateChange.REPLACE)

to redirect to the search screen doesn't work as (I guess the same short circuit that in flow) prevents going from a screen with a key to the same screen with the same key (even if the asociated view changed)

so, I tried
2) resetting the view with
val top = Navigator.getBackstack(this).top<Any>() val newView = with (it) {newView(top)} L.error("state change top=$top newView=$newView") val container = containerViewGroup() ?: return container.removeViewAt(0) container.addView(newView, 0)

it works, but now, when In this screen, I do a Naviagator.getBackstack(this).goTo(OtherScreenKey())

I get an exception, this one :
java.lang.IllegalArgumentException: The view [org.lakedaemon.dictionary.flow.JapaneseSearchView{421e1af0 V.E..... ........ 0,0-2560,1550 #7f1000fe app:id/flowSearchView}] contained no key!
at com.zhuinden.simplestack.BackstackManager.persistViewToState(BackstackManager.java:244)
at com.zhuinden.simplestack.navigator.Navigator.persistViewToState(Navigator.java:288)
at com.zhuinden.simplestack.navigator.DefaultStateChanger$NavigatorStatePersistenceStrategy.persistViewToState(DefaultStateChanger.java:75)
at com.zhuinden.simplestack.navigator.DefaultStateChanger.performViewChange(DefaultStateChanger.java:556)
at com.zhuinden.simplestack.navigator.DefaultStateChanger.performViewChange(DefaultStateChanger.java:541)
at com.zhuinden.simplestack.navigator.DefaultStateChanger$2.stateChangeComplete(DefaultStateChanger.java:524)
at com.zhuinden.simplestack.navigator.DefaultStateChanger$NoOpStateChanger.handleStateChange(DefaultStateChanger.java:42)
at com.zhuinden.simplestack.navigator.DefaultStateChanger.handleStateChange(DefaultStateChanger.java:517)
at com.zhuinden.simplestack.BackstackManager$1.handleStateChange(BackstackManager.java:76)
at com.zhuinden.simplestack.Backstack.changeState(Backstack.java:306)
at com.zhuinden.simplestack.Backstack.beginStateChangeIfPossible(Backstack.java:272)
at com.zhuinden.simplestack.Backstack.enqueueStateChange(Backstack.java:254)
at com.zhuinden.simplestack.Backstack.goTo(Backstack.java:164)
at org.lakedaemon.android.AndroidKt.goToScreenFor(Android.kt:594)
at org.lakedaemon.dictionary.UtilsKt.goToScreenFor(utils.kt:25)
at org.lakedaemon.dictionary.list.WordViewHolder.onSingleTapConfirmed(WordViewHolder.kt:48)
at org.lakedaemon.android.listener.DoubleTapListener.onSingleTapConfirmed(utils.kt:40)
at android.view.GestureDetector$GestureHandler.handleMessage(GestureDetector.java:315)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5350)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)

What should I do to fix this ?

Think about possibility of convenience method using Application.ActivityLifecycleCallbacks to manage BackstackDelegate's lifecycle integration

Excluding the onRetainCustomNonConfigurationInstance() which retains the internal BackstackManager, it can devour the following 4 callbacks:

  • onPause

  • onResume

  • onSaveInstanceState

  • onDestroy

Of course, this is an API 14+ method, and simple-stack takes it seriously that most of its functionality should work from API 1+ (just because it can).

So I gotta think about this one.

Composite Keys and subscoped services

Simple-Stack should allow either the creation of scoped services, or the integration with such a scoping system.

Currently the list represents a list of states, but sharing between them is difficult.

It should be possible to create additional views associated with keys inside a given state, and preserve their states automatically as well, without having to delegate the states to their parent.

How do you replace the current top item in the backstack?

I have 2 fragments and the operation would be like this

fragmentA opens fragmentB
fragmentB, makes an operation
fragmentB, finishes or closes and passes data to another instance of fragmentA
fragmentA(old) will be closed and replaced with fragmentA(new) to imitate that the view has been updated through the use of fragmentB

Right now, I'm thinking of using this:

Backstack backstack = backstackDelegate.getBackstack(); // lifecycle integration
backstack.setHistory(HistoryBuilder.single([key of fragmentA(new)]), StateChange.REPLACE);

But I'm afraid that the whole backstack will be cleared when I do

Out of the box support for "goUp()"

Up navigation is tricky if you actually want to handle it and not just say onBackPressed().

While it is easy to do with checking the history and accordingly do a goBack() or a setHistory(), the library could support this operator for the sake of clean parent navigation.

Handle process death.

Hey just pulled down simple-stack and wanted to play around with it. Pulled down the repo and tried out simple-stack-example-services but noticed that the stack doesn't persist after process death.

Steps to reproduce:

  1. Open simple-stack-example-services
  2. Click "Go to B"
  3. Put the app in the background.
  4. Kill the app through the multitasking pane
  5. Open simple-stack-example-services, notice that you are no longer on B and are instead back at the start.

Navigation within multistack activity.

Hi,

I did not find another way to contact you, so I decided to create an issue.
I have single activity app with implemented multistack feature.

In my app, I have SearchResultView which is accessible from two different places. From HomeView and from SearchView. SearchView and SearchResultView are in the same stack. SearchView is set as the default view for this particular stack( SearchStack ) . HomeView is placed as the default view for HomeStack.

I am using your default StackChanger implementation for multistack example where I have added view change animation.

The problem I have is when I am changing the view from HomeView ( added to HomeStack) to the SearchResultView ( added to SearchStack). SearchResultView is not a default view for his stack so when the animation is performing I firstly can see SearchView ( default for his stack) and after that SearchResultView is showing on the screen.

The question is how to change view from HomeView ( HomeStack) to SearchResultView ( SearchStack with SearchView as default) without showing default view for SeachStack ( SearchView )

Thanks for help
Best
Damian

Handling multiple views properly

I'm wondering that how to handle multiple view situations properly. Lets assume we have 2 activities, where the first has a bottom navigation with 3 screens (fragment/custom view), and the second is a just simple activity.

With this idea, these activities are converted to custom views(screens) and these screens are replaced in the hosting activity We can go forward and backward between them and save their status.

But what about the 3 screens in first screen. They should be part of stack mechanism individually ? Or please correct me if i'm mixing the things in my mind.

How to use simple-stack with objects parceled through johncarl81/parceler?

We have an existing project we need to incorporate the library in. The problem is, I would like to have put objects in the Keys. These objects does not implement Parcelable and I don't think I can refactor all of them into using Auto-Parcel as it is a pretty big project. Is there a way around it? Or how can I use simple-stack without relying on auto-parcel?

Child keys and shared services

Simple-Stack should allow either the creation of shared services, or the integration with such a service system.

Currently the list represents a list of states, but sharing between them is difficult.

It should be possible to link multiple views together so that they can share data and state with each other without having to create a message queue to communicate between state changes - the service should exist immediately, provided for the view, and associated with a given tag. (For example dialogs, or flows that last for longer than a single screen, but share the same data).

Think through possibility of introducing MessageQueue into BackstackManager

Oftentimes you want to communicate back to the previous UI state that a result has happened. This should be possible by adding an additional argument to goBack() and possibly the other navigation methods.

The question is:

  • should it be a StateBundle or an Object?

  • should parcellation use KeyParceler? The name won't really match the intention. :|

  • consider hiding Backstack constructor, most of the time you need BackstackManager or its wrappers anyways.

Proper java doc over classes / methods

Currently not a lot of code is documented.

While it is obvious to me what's happening, it's not obvious to developers who are trying to use the library, but aren't familiar with its internals.

going back in multistack

Hi,
I'm using your multistack implementation from sample in my app. My problem is, that I've got a screen that is accessible from almost every stack (the multistack is implemented in bottom navigation tabs if it's important). From this screen I can go to the same screen, in such case that I've got project details and similiar projects inside, so when I click on one of the similiar projects, I go to the same screen as the previous one. Going further, when clicking back, I want to omit all the previous project details screens and go back to the screen, in which I've chosen the first project detail. My question is: is there a way to do that in a simple way different from setting stack history explicitly? Another question is: is there any simple way to push some parameters when going back in stack? I want to have behaviour like "onActivityResult".

Check what is needed to create reliable Observable wrapper over CompletionListener

If the state changer skips the handling of the event, then completion listener is called immediately and synchronously. This allows the possibility of wrapping CompletionListener in Rx Observable, and handle all state change as a subscription, instead of inside StateChanger callback.

While this means all state changes are immediate, and backpressure occurs.

This behavior would end up as Okuki's GlobalListener.

Question is, in this case, is there any need for a BehaviorRelay, or as changes are immediate, there is no way for onPause to detach the state changer (or the completion listener)?

Enhancements for MVVM sample

1.) re-add tests if I'm not 100% lazy (although this is primarily relevant only after 2.)

2.) the repository is garbage, should be replaced with something reactive (but NOT rx-based. Just change listeners confined to the UI thread are fine) done with LiveData

3.) ListView should be RecyclerView, but it broke when I made it a recycler view for some rather unknown reason. Investigation required.

4.) a remote data service that uses MockWebServer instead of this random garbage would be nice so that Retrofit is also included.

5.) finish the TODO for inter-fragment comm. with message queue done

Provide nested key and scoped key built on top of service-tree with general-purpose scoped service manager (2.0)

Service-Tree (once finalized? although it's getting closer and closer now) allows storing services that survive config change for each scope.

Scopes need to be bound based on keys.

Hierarchical navigation needs to keep all top keys, and the current active top's service chain executes the backstack back op.

So both "hierarchical navigation out of the box" and "automatic scoped services" require service-tree. This is all implemented in nestedstack sample though.

But should it come with DefaultStateChanger? Or should it be opt-in? Hmm....

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.