LMFR Mobility team‘s journey to open source

# ~ curl http://lmfr.io/florianpaillard
{
    name: "Florian",
    company: "Leroy Merlin France",
    function: "Mobile lead developer",
    beginDate: "2014"
    team: "Mobility team / Colibri team",
    lm-projects : {
        pandroid : "open source mobile archetype",
        lm-library : "lm fr archetype extension"
        delivery : "open source delivery plugin",
        colibri-apps: "store apps",
        innovation: "htc vive, wear os prototyping ...",
        ...
    },
    other-projects : {
        robolytics : "mobile analytics tag manager",
        one-jitsu : "Jiu-jitsu practitioners network",
        booth-app : "FatBooth, AgingBooth, ...",
        ...
    },
    links : {
         twitter: "@paillard_f"
         linkedin : "http://lmfr.io/florianpaillard",
         robolytics : "https://robolytics.sertook.com",
         perso : "http://sertook.com"
    }
}

DISCLAIMER

Mobile
Team

Standard developement

Mobile developement

VS

Team diversity

LM

FR

CONTEXT

Mobility Team

3 mobile developpers

2014

3 - 5 apps

BAMBOO

WEB TEAM

Roadmap LM 2015

Gestion (backo mobile)

Vente (pyxis mobile)

Leroy Merlin France Apps (ios android)

Mobility Team

+ 30 mobile developpers

2018

+ 40 apps & backend

AMGP

to sum up

growing team

differents skills

large roadmap

no shared architecture

1 app = 1 architecture

Mobile Archetype

Abstract libraries

Navigation pattern

LMFR tools (Passport, Scanner, ...)

Design

 

 

 

Shared architecture

FAT

PRISONER

Solution

=
+

LM Library

Open source

Easy configuration

Optional dependencies

Performance

Security

Documentation

LM Library

Inner source

Colibri fonctionnalities

  • sso
  • scanner
  • analytics
  • preferences
  • micro services - (talk Adrien Leroy)

UI Guidelines

OPEN SOURCE

Open source

  • Collaborate with others
  • Developer commitment
  • Better visibility for recruiting developers

MobileTribe

PANDROID INSIDES

Basics

Dependencies Injection

  • Dagger
  • App base component
  • Auto injection

Architecture

  • Single responsibility principle
  • Screen inter-dependencies
  • MVP pathern

Basics

Event bus

@OnClick(R.id.event_send_to)
public void sendToSecondFragment() {
    eventBusManager.send(
        "My message", 
        "EventTag", 
        EventBusManager.DeliveryPolicy.AT_LEAST_ONE
    );
}


@EventReceiver({"EventTag"}) //Generate provider with a tag to filter event
public void onReceiveMessage(@EventTag String tag, String msg) {
    logWrapper.d(TAG, "message with tag '"+tag+"' received : " + msg);
}

Basics

LifecycleDelegate

public class IcepickLifecycleDelegate extends SimpleLifecycleDelegate<Object> {

    @Override
    public void onCreateView(Object target, View view, Bundle savedInstanceState) {
        super.onCreateView(target, view, savedInstanceState);
        Icepick.restoreInstanceState(target, savedInstanceState);
    }

    @Override
    public void onSaveView(Object target, Bundle outState) {
        if (viewExist)
            Icepick.saveInstanceState(target, outState);
    }
}
public class MyFragment extends PandroidFragment<FragmentOpener>{
    
    @BindLifeCycleDelegate
    IcepickLifecycleDelegate icepickDelegate;

//...

Basics

LifecycleDelegate

reviewManager.getReview("1", new NetActionDelegate<Review>(this) {
    @Override
    public void success(Review result) {}

    @Override
    public void onNetworkError(int statusCode, String errorMessage, String body, Exception e) {}
});




reviewService.getReview("1")
    .rxEnqueue()
    //bind observer on lifecycle thanks to RxPandroidFragment
    .compose(this.<Review>bindLifecycle())
    .subscribe(
    //...
    );

Optional libraries

Gradle dsl

pandroid{
    library('mylib') {
        libParam1 'param1'
        libParam2 'param2'
    }
}

Auto implement & bind to lifecycle

Optional libraries

GoogleAnalytics

FirebaseAnalytics

RxJava

Retrofit

Glide

Icepick

Crashlytics

Butterknife

SuperToasts

...

Navigation - Opener


//To handle the event
receivers.add(new FragmentEventReceiver()
                .setContainerId(R.id.main_content_container)
                .setAnim(FragmentEventReceiver.ANIM_MATERIAL)
                .addFragment(MaterialFragment.class)
                .setBackStackTag("material"));
public class MaterialFragment extends PandroidFragment<MaterialOpener> implements OnBackListener {

    @Override
    public void onResume(ResumeState state) {
        super.onResume(state);
        switch (state) {
            case FIRST_START:
                materialTransitionLayout.addAnimationWithViewId(opener.ivInfos, R.id.material_iv);
                materialTransitionLayout.addAnimationWithViewTag(opener.tvInfos, "coucou");
                break;
           //...
        }
    }
sendEvent(new MaterialOpener(ivInfos, tvInfos));

Kotlin dsl

adapter<String> {
    holder {
        layout = R.layout.cell_list
        itemType = 0 //your can use class has type, hashcode will be used
        binder = { view, data, index -> (view as Button).text = data }
    }
    holder {
        layout = R.layout.cell_list
        itemType = 1
        holderClass = CustomTxtHolder::class
    }
    holder {
        itemType = 2
        factory = CustomHolderFactory()
    }
    itemTypes { adapter: PandroidAdapter<String>, item: String, position: Int -> position % 3 }
}

Logger

Presenter

Templates

Security

Async

Animation

Storage

And more

Lifecycle

LiveData

ViewModel

Navigation

 

 Good tools ?

DELIVERY

Make your continuous delivery faster and easier

  • powerful workflow

  • simply build and sign artifacts

  • archive all you need into space

THANK YOU

Made with Slides.com