Code Monkey home page Code Monkey logo

pilot's People

Contributors

bryant1410 avatar doridori avatar waffle-iron 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

pilot's Issues

Dagger Examples

Not priority as 3rd party and relativly simple but if anyone asks for dagger examples be away of below notes

Q. Runtime dagger setup to add and remove stuff from dependency graph

Dagger 1 has objectGraph which can be adding to (& removed?)
Dagger 2 has components which can define their own scope names - these are statically analysed by modules & components using the sane scope

Would need to create components for each scope and injectable classes need to be able to be injected with this component. Does it need to grab a ref to it like the object graph?

ATM it would be easier to have global scopes which are cleared and managed by layers in the pilot stack i.e. SessionData, AddingCard, AuthingTxn which everything inside of those access. Is it better to keep as current and pass it around to everything that uses? Less clear when it should be wiped and when it will cause issues - easier to manage this in a stack arrangement

Improve README examples

Highlight the areas of android dev that are made easier using Pilot via a new docs section.

This can exist in a doc folder under VCS (rather than wiki) and should show a page for each solved issues with a before pilot and after pilot example.

Link from main readme

RFC-8: Best way to retain PilotStack in RootActivity

Currently the PilotStack is persisted via

@Override
    public final PilotStack onRetainCustomNonConfigurationInstance()
    {
        return mPilotStack;
    }

and reinstated via

    /**
     * This grabs the state stack that survives config changes (but not process death which requires
     * serializing or parceling the stack). The {@link PilotStack} could live in an app wide Singleton
     * but its nice to have it linked to an Activity as it can be GCd when the activity is killed.
     *
     * @return true if the application is starting fresh
     */
    private boolean ensureStateStackPresent()
    {
        PilotStack persistedPilotStack = (PilotStack) getLastCustomNonConfigurationInstance();
        boolean freshStart = persistedPilotStack == null;

        if(freshStart)
            mPilotStack = new PilotStack();
        else
            mPilotStack = persistedPilotStack;

        return freshStart;
    }

which allows it to live through config changes.

However, I imagine this will still work if the Activity is killed in the background (can test with dev settings) but prob not if the process is killed in the background due to mem constraints. However in this case we may need to persist the stack in savedState bundle anyhow (or parcel).

If find that when activity killed in bg object is not retained could actually make stack static as long as its cleaned up properly when Activity is actually destroyed for good.

If doing this will need to clear stack down in onDestory (if finishing = true) AND this needs to result in some cleanup lifecycle method being called in all frames (popped will suffice) POP all!

Clearing down of other UITypeHandlers when another handler takes priority

Now that the PilotSyncer can take multiple UITypeHandlers, each representing a subset of PilotFrames handled b the application there needs to be some notion of synchronization between then. For the case where one UI handler is about full screen views and one is about dialogs - the handlers need to be able to decide when to clear down its own views.

Take the following example. The stack is made up of frames

A, B, C

where A, B handled by a UITypeViewHandler, and C is handled by a UIDialogTypeHandler. Lets say D then is pushed on the stack, which represents a full screen view and we want to hide the dialog as its not currently top of the stack. UIDialogTypeHandler needs to have a callback saying something else has rendered a full screen view, so it can clear down its own views.

This is related to #19 which also needs to be aware of what views are full screen in for display logic.

Again we need to query if the new view is full screen - if yes, then all other UITypeHandlers should have a clearAllUI() call. If no then no call needed. UITypeHandlers will default to values depending on the type handled i.e. the UIVIewTypeHandler should return true by default for isFullScreenUI(Frame) whereas a UIDialogTypeHandler would return false. This should be overridable as an implementor may want some select views to return false.

Maybe UITypeHandler.isUIOpaque() would be better than UITypeHandler.isUIFullScreen()

You may be wondering what the point is of clearing other views when a new full screen view is rendered. Reasoning is that we may have a ViewHandler rendering into a rootView, a FragmentHandler rendering into a container view, and a DialogHandler raising dialogs. With multiple handlers there is no simple way of sorting the Z ordering of these respective root views.

Logging

Should be able logging which prints out the txn and stack state.

Add note on presenter state handling

Views can do this in onAttach / onDetach but controllers need to handle conveying current state at any point i.e. some controller methods may be missed. See Dynamo and others

T-50: PilotActivityAdapter should be removed?

If we remove/change the lifecycle stuff (see other issue) then this leaves the adapter as an extra layer of confusion in the Pilot Impl. Some of the logic could be removed if just done when the pilot stack is first created (launch frame creation) and the host activity to just add and remove the listeners itself?

T-47: Remove annotations for declaring what Frames back a view

Ideally we would remove annotations for this declaration, as they cannot currently be obfuscated by tools such and proguard et al, and only covered by class encryption, which is easily bypassable for all tested products.

There are a couple of fundamental design problem that need to be solved here:

Something (lets call this the Coordinator) is listening to a StateMachine, and is performing UI changes based upon the current State.

Options around the view obtaining a State:

a) The Coordinator create the view and push the state to it (or push a state to an existing view)
b) The view reach out and grab an expected state from the machine

Thoughts on supporting multiple states as mentioned in #44:

  • if pushing The view needs some way to declare what states it supports, and the coordinator needs to be able to be aware of this and push those states to the view
  • if pulling ...unsure

The simplest approach for the moment here is probably just to remove the annotation, and have a View call super(PilotFrame) with the Frame class its interested in.

Doc - stress that any controller type can be used

i.e.

  • RxJava based
  • Presenter style
  • Controller style
  • Binders
  • Dynamo / Engine
  • Fsm -> Presenter
  • should probably add a simple LCE type presenter to examples so people can see some form of stateful controller and where it plugs into the frame (it is the frame!)
  • would be good to have a Rx LCE also

Improve/Update example projects

At the mo its just showing a presenter - data - presenter stack with back functionality and could be a bit clearer in the text it shows whats going on under the hood!

PilotFrame.isOnStack()

convenience method so frame can check if still on stack before performing stack operations. (replacement for getParentStack == null check)

D-40: PilotFrame.pushed() call should happen after the UISyncer has been made aware of the frame change

As PilotFrame.pushed() is a convenient place for a Frame to kick of any logic / processing, it should happen after the UI syncer callback takes place.

The current approach https://github.com/doridori/Pilot/blob/master/android/lib/app/src/main/java/com/kodroid/pilot/lib/stack/PilotStack.java#L102 is open to a racecondition. If we have a Frame perform a stack operation inside of pushed then with the current order, the UI syncer will receive these events in backwards order, as the second pushed frame will hit the notifyListenerVisibleFrameChange call first.

Future Improvements Section in Wiki/Readme

FUTURE IMPROVMENTS
(see paper notes also)

MULTIPANE LAYOUTS - quite easy - can emply strategy pattern in Pilot Activity for setView and rebuildViews. SetView should call on strategy depending on screen size and can maintain its own model of some compnent layout and do whatever logic it needs to work out where to set the new view and how to keep track of current. rebuildViews should be able to look at the stack and work out what the view state should be - this would be simple for single root views as they just look at the top mut multipane views will need to look at whole stack and build the model mentioned in setView above. This should be done when coming out of deserialisation OR when new acitivy created depending on if the lifecycle on the stack and lifecycle of re-finlated views is diffent or not.

TASKS - layer in stack that branches into tasks. One is names 'current' and traversal happens in this branch only . can switch branches (which causes callbacks)dfg

D-35: Refactor of how the UI syncer renders views and performs frame view lifecycle callbacks

//TODO should change below to *VISIBLE STACK UPDATED
//TODO on change, should get stack of visible frames that should be rendered on screen at the mo, and pass them in turn to the UI type handlers (who can choose if they need to be rerendered or not)
//at moment have problem as if any of the stack is updated, this method is called, and a view can get multiple visibility callbacks without a change in visibility status
//plus! if the top frame is partially transparent, and the frame underneath is removed, this will not cause that underlying render change at present.

See #19 & #5

RFC-42: Presentation Seperation

At present the PilotFrame object represents two concepts:

  1. An application State, which exists on a navigable Stack. A State can contains actions appropriate to that State.
  2. A ViewModel like construct, which holds a bean like PresentationModel and has user input event methods i.e. userDidX(). This also has view lifecycle methods called upon it.

We should split this up into a Presentation and StateFrame conceptualisation, so we have a clear SRP approach for the components, which allows for easy switching of Presentation depending on the display medium / format.

Responsibilities

StateFrame

  • Has Stack appropriate lifecycle events i.e. pushed(), popped()
  • Has State-centric actions i.e. doSomething
  • Holds data which is relevant to the state e.g. some long running operation state

Presentation

  • Has view appropriate lifecycle events i.e. isCurrentlyVisible(boolean)
  • Can provide an immutable PresentationModel to the View when any changes encountered.
  • Has view-centric actions i.e. someButtonPressed which would map to a State-centric action.
  • Obtains data which will only be used for display e.g. AppVersion

To-be-defined

  • How does the Presentation survive config-change?
    • Should it be attachable to the StateFrame by means of an abstract listener/observer interface?
    • Could use the new ViewModel approach and LiveData etc (only prob useful with activities rather than Views as uses headless Fragments under the hood.
  • How does a view declare it will show for a frame and how is the presentation object created? Maybe inside view and stick to frame annotation?

T-46: Remove all reflection for PilotFrame creation / Allow PilotFrames to be created with new by the caller

The reflective state creation is not really needed, as callers can create the new states before passing to the statemachine. This would also make call hierarchys easier to follow.

Some thoughts on this in #45

i.e. We dont really need Frame Factorys unless we are recreating the UI https://github.com/doridori/Pilot/blob/master/android/lib/app/src/main/java/com/kodroid/pilot/lib/stack/PilotStack.java#L443 which for the few times this is actually required, we can roll a custom solution ie. save out some data object and recreate stack or frame as appropriate. This would potentially mean passing in some Serialisable object to the new state on create, as we do with Args and adding the reflective stack recreation code which we are removing as part of this issue.

PilotStack operations Builder object

Keep coming across scenarios where being able to execute stack operations / transactions atomically would be nice. These fall into

single listener callback for group operations

Problem here is that if you addFrame then removeFrame the logic can get a bit annoying to work out if should notify listeners for both actions. Dont really want every stack op method to have a 'notifyListeners' param.

Also helps for notifying a was visible top of stack frame now not vis without duplicates

not nullifiing the parent stack for the next operation

Easy to accidtally do something like
getParentStack.clearStack
getParentStack.pushFrame
which throws an NPE on second call

understanding what txns are grouped would make stack txn logging easier

When looking at showing readable logging #33 for stack state and stack txns would be easier if the txn groups were atomic then could show StackState -> startTxn -> Ops -> endTxn -> StackState

not accidentally closing app

Sometimes the stack can be empty for a short time between transactions, which currently triggers a stackEmpty callback which pretty much always closes the app (which you would want if result of back presses). atomic txns would not let this happen!

Could look something like

getStack().startTxn()
.popTo(FrameName.class, INCLUSIVE)
.pushFrame(DataFrame.class, Args)
.pushFrame(TopFrame.class)
.notifyListeners(true) //default
.go();

RFC-7: StackFrame stack lifecycle callbacks

ACTIVITY LIFECYCLE ENUM - register with parent stack? when register observer pass boolean to pass curent state

  • ACTIVITY_STARTED
  • ACTIVITY_STOPPED (seperate from NOT_STARTED?)

STACK LIFECYCLE (frame overrides)

  • FRAME_TOP_OF_STACK called when newly pushed to top or something popped
  • FRAME_NO_LONGER_TOP called when something pushed on top, can ignore popped as below will be called
  • FRAME_REMOVED_FROM_STACK called when popped on own or part of group

optional Frame callbacks should be

frameStackLifecycleEvent(...)
hostActivityLifecycleEvent(...)

When a frame added to stack both these methods need to be called with current state. Then while in the stack they will be called with each state change until FRAME_REMOVED_FROM_STACK or application destroyed.

Lifecycle methods should also be called after a stack has been de-serialised.

ACTIVITY_* and FRAME_ lifecycle states should be gettable also.


FINAL FRAME LIFECYCLE

  • onAddedToStack (pushed) (called once)
  • onVisibleFrameStatusChange(boolean) (only visible if top visible frame of stack regardless of opacity and activity is started - can always query the stack if need any more logic on top of this)
  • onRemovedFromStack (popped) (called once)

Need to add this in core docs. Main concepts?

FINAL VIEW LIFECYCLE

A PilotFrame-backed View needs to know when the backing PilotFrame is available and when its updated.

When a PilotFrameLayout extending View is created is will have the corresponding PilotFrame passed to it. See PilotFrame.backingFrameSet

State-change listeners are added inside the PilotFrame backed PilotFrameLayout.onAttached and PilotFrameLayout.onDetached methods and will result in PilotFrame.updated() being called, which is where you should sync up the UI state with the PilotFrame state.

Views have backingFrameSet(ShowPadPresenter backingPilotFrame) and this should be used over onAttached as more explicit and less change of OS re-attaching views messing anything up :)

TODO Currently in 'Quick Start'. Move this to concepts and link

C-43: PilotLifecycleManager should delete specific Listeners

https://github.com/doridori/Pilot/blob/master/android/lib/app/src/main/java/com/kodroid/pilot/lib/android/PilotLifecycleManager.java#L103 should delete specific listeners rather than all. If the same stack instance is used across Activity instances e.g. after a config change, and the lifecycle methods interleave like the below:

  • NewActivity.onCreate()
    • PilotLifecycleManager.addListener()
  • OldActivity.onStop()
  • OldActivity.onDestroy
    • PilotLifeCycleManager.removeAllListeners()

then the NewActivity has its listener removed. remove should only remove the listeners that lifecycleManager added.

@DontPersist annotation

May be useful to flag a frame as one thats not persisted (and therefore any frameabove it also) on process death i.e. if it contains sensitive data

Favour composition over inheritance for PilotActivity functionality

When building anything that interacts with the Android lifecycle (via Activities or Fragments) it can be hard to favour a composition over inheritance approach as it needs to tie into these core methods (i.e. onRetain.. & onCreate.. etc.

However its not ideal to provide a BaseActivity as

  1. It currently extends FragmentActivity but users may be using AppCompat or otherwise.
  2. Users may have their own base activity hierarchy they are stuck to allready.

There are two alternatives

  1. Provide a class which needs to be hooked up to the users current Root/Base Activity.
  2. Provide a headless fragment

Im inclined to go with 1 as much simpler and its a shame for View only apps to muddy the water with a Fragment!

T-51: StateFrameFactory onPostCreateFrame

should add a onPostCreateFrame method to the StateFrameFactory interface. This can allow for DI to take place with a Factory composed with some custom scoped Component, rather than reaching out for one.

Master / Detail View support

need to think about the best way to approach this.

Two options atm

  1. Handled in a view creator passed in at runtime #14. This would need to inspect the stack in some way and build the correct view representation of the stack based upon screen metrics.
  2. Support in the form of branched stacks, where there is a stack frame type that can hold a stack in itself i.e. Composite pattern. This could also be pushed at runtime for a certain presenter type that may represent the root of a master detail display. While this route is tempting is seems like it would be holding too much representation of the display structure inside the stack makeup. This is bleeding of concerns between concepts. This could cause issues say where a view was master detail in landscape and single screen in portrait - obvioisly the stack structure would persist on rotation but would then be awkward to handle with the new layout.

SRP - Break up the PilotManager

PilotManager is currently a bit bloated and has more than one responsibility.

Its two core responsibilities at present are

  1. To allow something with an Android lifecycle (i.e. Activity) to interact with something that is purposefully staying away from any lifecycle hassles (PilotStack). This involves listener management and handling delegated lifecycle methods.
  2. Maintains a list of View mappings and listens to stack changes - manifesting the top view mapping and setting it to a root view

The above obviously violates the Single Responsibility Principle (SRP) and should be changed. Pulling out the view manifestation logic into a separate class (with corresponding interface) would allow for easy switching for different approaches i.e. using a Fragment creator rather than view creator. This would make the lib more flexible also as not everyone likes View only approaches but most do suffer from (at least one of) the problems the abstracted stack solves.

This may also open the way for master/detail display as the view creator could be composed at runtime based upon device specs (i.e. screen size). #15

Also this may make it easier to cope with transparent view representations of frames. #5

Handling of non-opaque views (i.e. Dialogs) using a Rebuilder

The challenge if Dialogs are represented by stack frames is that we need to think about the notion of frames that are represented by transparent views / fragments / dialogs.

Simple stack listeners only really care about the top frame on the stack. However on view recreation it is not enough to simply look at the top frame of the stack and build the corresponding view, as the top frame may represent a dialog. The view code needs to be able to call down through the stack until it is satisfied that there is a view shown which is full-screen opaque.We do not really want to do this from the top of the stack down as if we had OpaqueView->Dialog1->Dialog2 in the stack and the top Dialog2 frame was rendered first, then Dialog1, then the opaqueView we would be misrepresenting the stack. It may be worth allowing the view code to flag the top-most frame it wants to display and then get callbacks for each frame from that flagged frame to the top of the stack.

Also want to keep in mind that frames should not have a notion of how they are displayed i.e. transparent or dialog etc. Two types of frames at the mo are Presenter frames (i.e. represent some user state) and scoped data frames.

T-52: Add ViewCreator.onPostViewCreated

  • Rename to ViewFactory
  • Add abstract onPostViewCreated, to allow DI to be performed external to the View, or a DI object to be passed to the View. This is preferred over the View grabbing the DI Component statically. Easier to testing.

CircleCI

Add to circle for test running and auto release from master

Update docs and improve segmentation

There have been a lot of changes since the README was written. Need to update the docs.

For ease of navigation should create a /docs and segment and link from the README i.e.

  • Architectural Overview
  • Control Flow
  • Show me the code
  • Guide to examples
  • state handling

Core premise - view lifecycle stuff should apply to views only - and some backend processes on a need to know basis. Should not have to worry about any async operations or state preservation when thinking about views OR backend stuff.

R-44: A View should be able to be shown for more than one state

Sometimes, we may want successive states to use the same view (e.g. a loading view) to show. At present using the annotation we can only specify a single View. Annotation should prob allow an array also.

Interesting thinking about how an interested view would receive updates for multiple state types:

  1. Could have a single method that takes a State and casts to what it expects (and checking if it handles multiple
  2. Could have multiple methods (extend multiple generic interfaces) i.e. StateListener<Type1>, StateListener<Type2>

T-45: We should split out the (Navigable)-State-Stack

The state-stack (https://github.com/doridori/StateStack) (comprised of state-frames) should live as its own android independent project.

Really its a type of state-machine, which:

  1. Allows states to exist in a stack, and have a notion of if they are at the top of the stack or not, and allow moving backwards through the stack.
  2. Has a notion of the state machine being active or paused (minimal lifecycle)
  3. Accepts listeners
  4. Has no notion of how states are created
    • There should be a stack operation builder as talked about in #36, but this should defer the actual building of a stack object in some abstract way, so the implementor can decide if this is with the caller creating the state or the state being created reflectivly (which is better suited for android lifecycle restoration aka the intent system)
    • Regarding new state creation, we need to not overthink this one. States can be created by anything (i.e. another State or some external code) in any way. There is some consideration thats needs to take place if the state machine should be easily serialisable / restorable, but this can be as simple as a rule saying States should only take primitive Arg objects in their constructors, which would allow some external serialiser to grab all the current class names and Args objects, and recreate the state machine from this info. Really this should be something contained in an F.A.Q and sample project as opposed to in the core lib, as if one does not care about this function there is no need to clutter the state creation logic just in case someone wants to save/restore.
  5. Querying of stack state and contents
  6. currently Allows multiple type of stack frame types
    • Currently this is done with baked in visible frame annotations. This should be removed, and the listeners (defined outside of the project) can contain the logic about how to tell if a frame should be rendered or not. (may involve writing a listener transformer, that takes events and may be able to convert them into visible events depending on the implementation)
    • I think this should be removed as the integrator can define this externally
  7. Logging of stack changes
  8. Stack operation builder for atomic stack operations (simplfies internal callback logic)
  9. Easy testing
    • states should auto have the parent state-machine set to it, and have the ability perform transitions on it directly. For testing, a mock state-machine can be attached to the state.

(see MM state map for more)

The android project would then define:

  • Listening for stack changes, and working out if a new view should be rendered.
  • View rendering animations
  • Pushing (or pulling) the state to any interested views
    • Pulling may be much simpler than current impl. Pulls when its created and does all its setup on successful pull. If it cant pull the state it wants its safe to assume the view is just about to be destroyed and a new one will take its place (that represents the current actual state). It seems that views are not recreated on config-change unless the view is inside a fragment, so when fragment-less this should never really happen anyway.
  • Transforming the state via a Presentation layer #42
  • Restoring the stack state if needed (if states have been created via reflection and using Args)
  • Translating android lifecycle events into the active or paused events
  • Creating stack frames (and the DI approach e.g. using Providers hidden behind a builder as opposed to current field injection)

C-53: Improve PilotUISyncer class naming

PilotUISyncer is a rubbish name as it is not clear what it is doing.

StateStackRenderer would be more descriptive.

This is composed of various classes that handle a subset of states. The interface for these could be called StatesRenderer and implementations could be called RenderStatesToView, RenderStatesToDialog, RenderStatesToFragment etc.

Easier to work out what the above is doing from the class name for the above.

Remove Presenter keyword

More explicit naming and does not lead anyone to think it needs to represent a traditional Presenter.

In fact should change all the Presenter* class names to FrameBacked* or something

Change @presenter to @RepresentsPilotFrame

Persisting stack state

Unlike traditional god-activity / god-fragment approaches we do not really need to worry about persisting state on config change or activity tear-down when in the background, which cuts out a high % of state saving. We do however need to consider the case where process-death occurs and we want the application to be in a similar state to where we left it on return. This issue is regarding the manner in which to save the stack.

As I see it we have 2 options

  1. Serialize the stack (or parcel)
  2. Use a primitive representation of the stack

In turn will briefly talk about these.

Serialize the stack (or parcel)

Advantages

  • Preserves callbacks between stack objects
  • Can create frames directly via constructor with input args and anon callbacks (for any frame that requires a result)
  • could save the frames ViewState for free. (this may actually be useless as of course we cannot save the async ops that may be running and therefore the viewstate may show 'Loading' when nothing actually is.
  • Deserializing the stack is very simple

Disadvantages

  • Could be quite slow with large stack and frames
  • Need to mark all fields and inner classes (and anon) inside frame as Serializable or transient
  • Would need to have tests covering the serializability of each frame as otherwise easy way for crashes to sneak in

Use a primitive representation of the stack

This is much like the approach that Activitys and Fragments already use i.e. take a Parcelable Bundle that represents the inputs (and potentially any loaded data) used to start a frame and persist this. This makes it easy to reload the application to a very similar state to what it was when the process was killed.

Advantages

  • Simpler as only saves Bundle and frame class name. Do not have to mark everything up as Serializable or transient

Disadvantages

  • Would need to reimplement a similar kind of bundle saving / stack recreation as AOSP uses for activities / fragments. Would probably use reflection and no-arg / single arg frame constructors.
  • makes passing data bit more verbose as have to pass keys and primative values through (unless scoped data is being referenced)

Notes

  • makes callbacks more verbose as cannot pass anon classes through but rather would need a pushFrameForResult(CallbackToFrameClass, requestCode) kind of approach...to make this simpler could follow a pattern of having a calling frame implementing a result interface which is then pulled from the stack when a result is ready (looking at the stack item below the one passing the result and checking if it conforms to the correct interface).This would still work after dead-process recreation and would avoid the result codes and request code stuff from current AOSP Activity solutions.
  • Would also need to allow arbritrary stuff to be saved in bundle aka onSaveInstanceState

Conclusion

Primitive stack representation makes more sense due to the pros and cons above.

Implementation

Callbacks

Going will a manual inspect stack and pull frame with callback interface approach.

Primitive bundle

  • To support this PilotFrame subclasses need to be able to have a Bundle delivered to them. Either a construtor arg called via super() or an initBundle() method. May use a no-arg constructor in PilotFrame that throws an error as the bundle constructor should explitly be called - this will protect against subclasses forgetting to call super.
  • Bundles should be passed to optional onSave and onRestore methods by the stack on process death / recreation
  • PilotStack should handle grabbing the Frame type and bundle and storing this for stack persistence on process death.

Cannot find symbol class PilotSyncer

Hello,
Trying your PilotExample. I am compiling against your latest Pilot lib com.kodroid:pilot:0.9.0-SNAPSHOT. I am getting a build error in Android Studio 2.2 Preview 6 from ExampleRootActivity.java:

Error:(15, 37) error: cannot find symbol class PilotSyncer

Notion of being able to rebuild the ui from a certain point in the stack when an Activity is restarted

Some UI concepts i.e. Master (static not sliding) / Detail #15 or a Dialog have a notion that the top frame of the stack does not represent the entire visible UI. This can cause issues in a few cases but easiest to observe when you have a frame stack like

A, B, C

where C may be a dialog and A, B full screen views. A naive view implementation would show the top frame C in onCreate, but this would cause issues if B was not already showing as the visible views would not represent the actual stack, which we want to do at all times. The same would be true if B represented a Master view inside a Master/Detail hierarchy.

One approach we can use to solve this is that when a new Frame is encountered i.e. the topFrameChanged callback is about to be invoked, we need know from what point the callbacks should be make to any waiting to render UI.

To do this we could backtrack through the view stack - calling on the relevant viewTypeHandlers and asking 'is a UI visible for frame x where frame x is a full screen view`. The first full screen view we encounter needs the be rendered to screen first, and then everything above it in the stack needs to be rendered.

Lets say C above is a frame represented by a full screen view - when C hits the top of the stack - the UITypeHandlers are queried to see if C is represented by a full screen UI for the current configuration:

IF C is full screen
    IF UI for C is already visible, RETURN
    ELSE render C, RETURN
ELSE 
    perform check on next frame _down_ in the stack until its rendered, then render all frames above it in the stack.

Fragment Support

Currently only supports View based architectures and old school dialogs. Would be good to extend support to Fragments and FragmentDialogs. Due to the SRP #14 refactors UITypeHandlers can now be registered for differnt UI types (i.e. Views, Dialogs, Fragments etc), this leaves the challenges around...

1.Working out where in the Fragments lifecycle is safe to start using the Presenter.
2.Working out where in the DialogFragments lifecycle is safe to start using the Presenter. This has an extra constraint due to where onCreateDialog() is called
3.An example of how to align the foregound view for an Activity that used both a UIViewTypeHandler and a UIFragmentTypeHandler

Ideally Fragments onCreateView would have access to the Presenter so any views can be initialised with frame based data. This needs to be true for

a) newly placed fragments
b) fragments that have been recreated after a config-change

Need to keep in in mind the horror that is http://staticfree.info/~steve/complete_android_fragment_lifecycle.png. Ideally would not use fragments at all but sometimes one needs to (say a 3rd party lib that supplied part of its SDK pre-bundled as a fragment)

On Fragment recreation - cant really reliably push the Presenter to the Fragment before onCreateView is called, as this is called as a result of Activity.setContentView, inside of which is also the fragment recreation step.

(http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/com/android/internal/policy/impl/PhoneWindow.java/?v=source)
delegates to
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/transition/Scene.java
delegates to
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/view/LayoutInflater.java

May resort to Fragments needing to operate on a Pull fashion instead of Push (Views have their presenters pushed to them on creation). inside Fragment.onCreateView and FragmentDialog.onCreateDialog the first call could grab the corresponding Presenter from the hosting Activitys PilotStack. It would also need to cope with the event that the frame is no longer top of the pilot stack, in which case the fragment should dismiss itself asap.

Beginning to think to not bother - an arbritrary fragment can be shown using the GenericUITypeHandler and handled manually...

EDIT: maybe we dont have to worry about fragment recreation if the UIFragmentTypeManager is just using the FragmentTransaction.replace() method anyway. If fragment state restoration is handled in the same was as Views may should still restore fragment test, Test!

Just done some testing - if we use replace with the same fragment the state is not saved by default - if not using replace then the presenter is after onCreateView...

Think will need to just pull the presenter (if not already set) and if its not the top of the stack then it will auto be switched out by the top of stack callback that will follow the root Activitys.setContentView call. Then all need to do is ensure what ever UITypeHandlers that are being used will clear each other / take priority.

Was thinking a Fragment can pull from the top of the stack using its @Presenter annotation to declare what presenter its interested in. If the top of the stack is a different presenter/frame null will be passed. This can be ignored and the fragment would build no UI in this case - as its about to be switched out anyway (it no longer represents the top of the stack). This wont work at current as after process death we may have some fragments recreated but the PilotLifeCycleManager has its onCreateDelegate called after Activity.setContentView. This is intentional as we need to pass the PilotSyncer in here, which can reference part of the UI (see UITypeViewDisplayer). If going this route would need to ensure that the PilotStack was already accessible by the point setContentView is called. This could be done by delegating two calls to PilotLifecycleDelegate inside Activity.onCreate, one before setContentView which just uses the savedStateBundle to restore the stack, and one after that passes the ui hooked up PilotSyncer

Will create a new branch for this as while simple fragment usage is handy - Its not the primary use-case for this lib.

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.