The current LockScreen in Gaia is a monolithic component, which is even the second large file in the System app according to its LOC. This make it hard to reasonably add/modify/remove functions and even change styles.
Although the sliding function had been pulled out as a standalone shared library since v1.3, we still need to break the whole LockScreen down to make it more flexible and isolate its inner heterougeneous components, like the clock and the music player. These components should be individual units that only manage themseleves and communicate with LockScreen with some public interfaces, rather than directly grab their needs and change the global states like the current way they use. In other words, they should be widgets, and managed by the LockScreen widget system. The whole refactoring plan now is on going and scheduled as a part of Gaia v1.5, and this article would focus on explain what architecture we would use, and how to implement a new widget.
Mediator, Factory and Router
In the new LockScreen, we would have three different components to manage widgets and communications:
Mediator: the coordinator of whole LockScreen, and it's also the only one component should be instantiated explicitly
Factory: create new widgets in a centralized and clearly way
Router: send and receive messages to communicate with the outside world
The mediator would handle all requests from widgets. These limited requests include:
- Unlock: widget require to unlock; if there is no objection (see Unlocking Auditor), the LockScreen would be unlocked
- Lock: widget require to lock; however, since widgets may not live before we lock the screen, this request may be useless
- Canvas: widget require a DOM element to draw itself; some widgets may not need to fire this request
- Invoke: widget require to invoke another widget, or secure app, or web activity
When mediator received these requests, it may change the LockScreen's states (locking/unlocking requests), and notify all widgets the change. Notifications like this is different from the request/response way, and they're designed as another channel to communicate with widgets according to the Observer pattern.
From the IO side, the mediator would also forward messages from router to widgets as notifications, and wigets can post messages via mediator, which would forward it to the router. The principle behind this is that widgets should only communicate with the mediator, and know nothing in the LockScreen beyond it.
In the launch path, mediator is also the first component initialized. It would creare router and factory during its initialization, and would bootstrap default widgets which now are hard-coded inside the mediator itself. In the future we may provide a customizable way to set the widget list before we bootstrap the whole LockScreen.
Widgets should be instantiated by the factory, which would pass the mediator as a parameter to the widget constructor. After initialization, widget should register itself to the mediator.
Customed widgets should handle nothing about this, because the basic widget would handle these common things automatically. So it's strongly recommended that developers should only create their widgets based on the basic widget.
Widgets may fire requests and receive messages from the mediator and the router (forwarded by the mediator), and it may also post some message out. The following diagram shows these communications:
This diagram can be explained as follow:
- Widget may post message to outside world. This message would be forwarded by the mediator to the router, so the widget can still know nothing about router.
- Router may receive and forward some messages to the mediator, which would forward it again to widgets. In some rare cases, the message is for the mediator, so the mediator would not forward these message to widgets.
- When the states of LockScreen get changed, mediator would notify the changes to all widgets, too. These changes may come from some widget requests (like Lock/Unlock).
- Widget may require to unlock the LockScreen. When received this request, the mediator would ask all unlocking auditors to see if there is any objection. If there is no objections, like the wrong passcode detected, the LockScreen would unlock itself.
- Some widgets may invoke secure app, like the secure camera in the passcode panel.
- Widgets can also send requrest to execute some web activity. In this case, the mediator would unlock first. If success, the activity would be fired.
- Some widgets may require a canvas to draw itself. When received the request, the mediator would prepare the element for the widget. A future feature is to isolate each widgets in its own iframe, so that the widget can be isolated better than the current mechanism.