
BUILD A BROWSER BY KOTLIN COLIN LEE SEBASTIAN KASPARI @colinmlee @Anti_Hype Copenhagen Denmark Feature Session Toolbar Downloads Contextmenu Customtabs Findinpage Webnotifications Reader View Search Sync Prompts Sendtab QR ... Browser Toolbar Engine-System Engine-Gecko Search Tabstray Menu ... Concept Engine Toolbar Storage Fetch Push Tabstray ... UI Service Support Lib Autocomplete ... ... ... State Crash ... Agenda ● Why? ● How? ● Live code a browser! ● Syntactic Sugar Why? Firefox for Android Firefox for Android Focus for Android Firefox for Android Firefox Rocket Firefox Lite Focus for Android Firefox for Android Firefox Rocket Firefox Lite Focus for Android Firefox for Fire TV Firefox for Echo Show Firefox for Android ? Firefox Rocket Firefox Lite Focus for Android ? Firefox for Fire TV ? Firefox for Echo Show Firefox for Android ? Firefox Rocket Firefox Lite Focus for Android ? Firefox for Fire TV This does not scale! ? Firefox for Echo Show Goals ● Share code between our apps ● Reduce the maintenance overhead ● Clear architecture boundaries ● Make it easier and faster to build new or experimental products. Solution: Build independent, reusable components to share code between existing projects and build new apps faster. Components? ● Independent open-source Android libraries ● “Android First” extensible application architecture ● Minimal cross-component and third-party dependencies ● Customizable and pluggable ● Distributed as AARs via a Maven repository Components? 100% Kotlin Components? 100% Kotlin Well, and some Rust How? Browser Engine HTML CSS Browser Engine JS Browser Engine WebView GeckoView Servo ... GeckoView GeckoView ● App developer in control of updates. ○ Only one version needs to be supported. No surprises. ● Multiprocess ● Separation of session (tab) and view ● Private mode ● WebExtensions ● Tracking Protection Engine Components browser-engine-system concept-engine browser-engine-gecko ... Toolbar Engine Components browser-toolbar concept-toolbar ... Done? What else? feature-downloads browser-contextmenu feature-media browser-awesomebar feature-readerview feature-downloads Feature Session Toolbar Downloads Contextmenu Customtabs Findinpage Webnotifications Reader View Search Sync Prompts Sendtab QR ... Browser Toolbar Engine-System Engine-Gecko Search Tabstray Menu ... Concept Engine Toolbar Storage Fetch Push Tabstray ... UI Service Support Lib Autocomplete ... ... ... State Crash ... LIVE CODING Syntactic Sugar Mozilla ❤ Kotlin null, null?, null!! Explicit nullability Handling state lib-state Store & State Store State data classes Modeling State data class BrowserState( val tabs: List<TabSessionState> = emptyList(), val selectedTabId: String? = null, val customTabs: List<..> = emptyList(), val extensions: Map<..> = emptyMap() ) : State lib-state Observing state Store State observes State Component / App lib-state Observing state @CheckResult(suggest = "observe") @Synchronized fun observeManually( observer: Observer<S> ): Subscription<S, A> { // .. } lib-state Observing state val subscription = store.observeManually { state -> // .. } subscription.unsubscribe() Extension functions Iterating on API @MainThread fun <..> Store<S, A>.observe( owner: LifecycleOwner, observer: Observer<S> ) { // .. } Extension functions Iterating on API @MainThread fun <..> Store<S, A>.observe( view: View, observer: Observer<S> ) { // .. } lib-state Dispatching actions Store State Action observes State dispatches Component / App Sealed classes Modeling Actions sealed class TabListAction : BrowserAction() { data class AddTabAction( val tab: TabSessionState, val select: Boolean = false ) : TabListAction() data class SelectTabAction( val tabId: String ) : TabListAction() // .. } lib-state Inside the store Store Action Reducer State State Functions & Sealed classes Reducing state fun reduce( state: BrowserState, action: TabListAction ): BrowserState { return when (action) { is TabListAction.AddTabAction -> { .. } is TabListAction.SelectTabAction -> { .. } // .. } } data classes Creating a new state val newState = state.copy( selectedTabId = "some-other-tab" ) Type aliases Observing state typealias Observer<S> = (S) -> Unit typealias Reducer<S, A> = (S, A) -> S Channels & Flow Channel Observing state sequentially @ExperimentalCoroutinesApi @MainThread fun <..> Store<S, A>.channel( owner: LifecycleOwner = ProcessLifecycleOwner.get() ): ReceiveChannel<S> { // .. } Flow Observing state @ExperimentalCoroutinesApi @MainThread fun <..> Store<S, A>.flow( owner: LifecycleOwner? = null ): Flow<S> { // .. } “Scoped flow” Observing state @ExperimentalCoroutinesApi @MainThread fun <..> Store<S, A>.flowScoped( owner: LifecycleOwner? = null, block: suspend (Flow<S>) -> Unit ): CoroutineScope { // .. } Operators for dealing with state updates Flow operators Only update UI if the state has changed fun <T> Flow<T>.ifChanged(): Flow<T> store .flow() .map { state -> state.selectedTab.title } .ifChanged() .collect { title -> // .. } Flow operators Only update UI if state changed partially fun <T, R> Flow<T>.ifChanged( transform: (T) -> R ): Flow<T> store .flow() .ifChanged { state -> state.selectedTab.title } .collect { state -> // .. } Flow operators fun <T, R> Flow<List<T>>.filterChanged( transform: (T) -> R ): Flow<T> store .flow() .map { state -> state.tabs } .filterChanged { tab -> tab.title } .collect { tab -> // .. } https://mozac.org https://mozilla.github.io/geckoview https://mozilla.github.io/application-services/ https://github.com/mozilla-mobile/ THANK YOU AND REMEMBER TO VOTE Colin Lee @colinmlee Sebastian Kaspari @Anti_Hype #KotlinConf.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages62 Page
-
File Size-