doridori / pilot Goto Github PK
View Code? Open in Web Editor NEWAn android.* decoupled application stack for Android
License: Apache License 2.0
An android.* decoupled application stack for Android
License: Apache License 2.0
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
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
needs call after presenter set so view can properly init - atm have to override setPresenter and do init after super()
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!
At the moment view transitions are not animated. Should allow an Animation instance to be supplied for in and out animations.
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.
Should be able logging which prints out the txn and stack state.
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
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?
feature/approach grid
See some projects with similar concerns here https://www.reddit.com/r/androiddev/comments/3pljle/what_exactly_are_fragments_used_for/
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:
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.
i.e.
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!
convenience method so frame can check if still on stack before performing stack operations. (replacement for getParentStack == null check)
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 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
...as it returns a View type from one of the methods for use only by the UITypeDisplayerView
, Each type displayer should contain its own interface def for this use-case
easier to test frame in isloation and mock stack to verfiy outbound calls and reduce side effects
//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.
At present the PilotFrame
object represents two concepts:
Stack
. A State can contains actions appropriate to that State.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.
StateFrame
Stack
appropriate lifecycle events i.e. pushed()
, popped()
doSomething
Presentation
isCurrentlyVisible(boolean)
PresentationModel
to the View
when any changes encountered.someButtonPressed
which would map to a State-centric action.Presentation
survive config-change?
StateFrame
by means of an abstract listener
/observer
interface?ViewModel
approach and LiveData
etc (only prob useful with activities rather than Views
as uses headless Fragments
under the hood.On restart the onCreateDelegate
is called and the stack listener is triggered with a null
visible frame. Write catching test and fix.
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.
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();
For PilotFrame
& PilotFrameLayout
. Add to main wiki.
Discussed in #7
Much easier to talk about when using the word state, as primarily the objects are states, its is secondary that they happen to be stored in a Stack.
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 poppedFRAME_NO_LONGER_TOP
called when something pushed on top, can ignore popped as below will be calledFRAME_REMOVED_FROM_STACK
called when popped on own or part of groupoptional 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
backedPilotFrameLayout.onAttached
andPilotFrameLayout.onDetached
methods and will result inPilotFrame.updated()
being called, which is where you should sync up the UI state with thePilotFrame
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
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()
OldActivity.onStop()
OldActivity.onDestroy
PilotLifeCycleManager.removeAllListeners()
then the NewActivity
has its listener removed. remove
should only remove the listeners that lifecycleManager
added.
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
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
FragmentActivity
but users may be using AppCompat
or otherwise.There are two alternatives
Im inclined to go with 1 as much simpler and its a shame for View only apps to muddy the water with a Fragment!
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.
need to think about the best way to approach this.
Two options atm
PilotManager is currently a bit bloated and has more than one responsibility.
Its two core responsibilities at present are
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
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.
ViewFactory
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.Add to circle for test running and auto release from master
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.
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.
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:
State
and casts to what it expects (and checking if it handles multipleStateListener<Type1>, StateListener<Type2>
May see performance benefits here.
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:
active
or paused
(minimal lifecycle)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.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:
Args
)active
or paused
eventsProviders
hidden behind a builder as opposed to current field injection)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.
..instead of PilotBackedFrameLayout
Will ease flexibility of using Views
| Fragments
down the line.
Also will ease supporting multiple Frames / states per view.
One is what it is the other is how it behaves...
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
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
In turn will briefly talk about these.
Serialize the stack (or parcel)
Advantages
Disadvantages
Serializable
or transient
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
Bundle
and frame class name. Do not have to mark everything up as Serializable
or transient
Disadvantages
Notes
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.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
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.onSave
and onRestore
methods by the stack on process death / recreationHello,
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
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.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.