android - Square Flow + Mortar tablet examples -


i've been experimenting using flow , mortar alternative architecture our android apps. i've been working on app @ minute single phone layout, wondering how flow , mortar architecture might work if want have different layout tablets. master details might simplest example, there other examples.

i have few ideas how work, wanted know square developers might have thought around topic.

we're still working on canonical answer this, basic idea let resource system change views you're showing in situation. activity sets content view to, say, r.layout.root_view. tablet version of layout (we put in res/layout-sw600dp) can tied different views, might inject different presenters, , on.

for cases need make runtime decision, define boolean resource in values/bools .xml

<?xml version="1.0" encoding="utf-8"?> <resources>   <bool name="show_tablet_ui">false</bool> </resources> 

and values-sw600dp/bools.xml

<?xml version="1.0" encoding="utf-8"?> <resources>   <bool name="show_tablet_ui">true</bool> </resources> 

expose rest of app via dagger. use binding annotation:

/**  * whether should show tablet ui.  */ @retention(runtime) @qualifier public @interface showtabletui {   int id = r.bool.show_tablet_ui; } 

and provider method like:

/**   * singleton because there's no reason read resources again,   * won't change.   */ @provides @showtabletui @singleton boolean showtabletui(resources resources) {   return resources.getboolean(showtabletui.id); } 

but wait there's more! suppose want have single screen / blueprint definition manufactures different modules different form factors. we've started using annotation scheme simplify kind of thing. instead of making our screen classes implement blueprint, we've started using annotations declare interface class. in world here's how screen can selectively choose modules use tablet or mobile.

@layout(r.layout.some_view) @withmodulefactory(somescreen.modulefactory.class) public class somescreen {   public static class modulefactory extends responsivemodulefactory<homescreen> {   @override protected object createtabletmodule(homescreen screen) {     return new tabletmodule();   }    @override protected object createmobilemodule(homescreen screen) {     return new mobilemodule();   } } 

magic, right? here's what's behind curtain. first, modulefactory static class given access screen and resources , spits out dagger module.

public abstract class modulefactory<t> {   final blueprint createblueprint(final resources resources, final mortarscreen screen) {     return new blueprint() {       @override public string getmortarscopename() {         return screen.getname();       }        @override public object getdaggermodule() {         return modulefactory.this.createdaggermodule(resources, (t) screen);       }     };   }    protected abstract object createdaggermodule(resources resources, t screen); } 

our trixie responsivemodulefactory subclass looks this. (remember how showtabletui.java defined resource id constant? why.)

public abstract class responsivemodulefactory<t> extends modulefactory<t> {    @override protected final object createdaggermodule(resources resources, t screen) {     boolean showtabletui = resources.getboolean(showtabletui.id);     return showtabletui ? createtabletmodule(screen) : createmobilemodule(screen);   }    protected abstract object createtabletmodule(t screen);    protected abstract object createmobilemodule(t screen); } 

to make go, have screenscoper class (below). in mortar sample code, you'd make screenconductor use 1 of these create , destroy scopes. sooner or later (soon hope) mortar and/or samples updated include stuff.

package mortar;  import android.content.context; import android.content.res.resources; import com.squareup.util.objects; import dagger.module; import java.lang.reflect.constructor; import java.lang.reflect.invocationtargetexception; import java.util.linkedhashmap; import java.util.map;  import static java.lang.string.format;  /**  * creates {@link mortarscope}s screens may annotated {@link withmodulefactory},  * {@link withmodule} or {@link module}.  */ public class screenscoper {   private static final modulefactory no_factory = new modulefactory() {     @override protected object createdaggermodule(resources resources, object screen) {       throw new unsupportedoperationexception();     }   };    private final map<class, modulefactory> modulefactorycache = new linkedhashmap<>();    public mortarscope getscreenscope(context context, final mortarscreen screen) {     mortarscope parentscope = mortar.getscope(context);     return getscreenscope(context.getresources(), parentscope, screen);   }    /**    * finds or creates scope given screen, honoring optoinal {@link    * withmodulefactory} or {@link withmodule} annotation. note scopes created    * unannotated screens.    */   public mortarscope getscreenscope(resources resources, mortarscope parentscope,       final mortarscreen screen) {     modulefactory modulefactory = getmodulefactory(screen);     mortarscope childscope;     if (modulefactory != no_factory) {       blueprint blueprint = modulefactory.createblueprint(resources, screen);       childscope = parentscope.requirechild(blueprint);     } else {       // need every screen have scope, injects scoped.  need       // if screen doesn't declare module, because dagger allows injection of       // objects annotated if don't appear in module.       blueprint blueprint = new blueprint() {         @override public string getmortarscopename() {           return screen.getname();         }          @override public object getdaggermodule() {           return null;         }       };       childscope = parentscope.requirechild(blueprint);     }     return childscope;   }    private modulefactory getmodulefactory(mortarscreen screen) {     class<?> screentype = objects.getclass(screen);     modulefactory modulefactory = modulefactorycache.get(screentype);      if (modulefactory != null) return modulefactory;      withmodule withmodule = screentype.getannotation(withmodule.class);     if (withmodule != null) {       class<?> moduleclass = withmodule.value();        constructor<?>[] constructors = moduleclass.getdeclaredconstructors();        if (constructors.length != 1) {         throw new illegalargumentexception(             format("module %s screen %s should have 1 public constructor",                 moduleclass.getname(), screen.getname()));       }        constructor constructor = constructors[0];        class[] parameters = constructor.getparametertypes();        if (parameters.length > 1) {         throw new illegalargumentexception(             format("module %s screen %s should have 0 or 1 parameter", moduleclass.getname(),                 screen.getname()));       }        class screenparameter;       if (parameters.length == 1) {         screenparameter = parameters[0];         if (!screenparameter.isinstance(screen)) {           throw new illegalargumentexception(format("module %s screen %s should have "                   + "constructor parameter super class of %s", moduleclass.getname(),               screen.getname(), screen.getclass().getname()));         }       } else {         screenparameter = null;       }        try {         if (screenparameter == null) {           modulefactory = new noargsfactory(constructor);         } else {           modulefactory = new singleargfactory(constructor);         }       } catch (exception e) {         throw new runtimeexception(             format("failed instantiate module %s screen %s", moduleclass.getname(),                 screen.getname()), e);       }     }      if (modulefactory == null) {       withmodulefactory withmodulefactory = screentype.getannotation(withmodulefactory.class);       if (withmodulefactory != null) {         class<? extends modulefactory> mfclass = withmodulefactory.value();          try {           modulefactory = mfclass.newinstance();         } catch (exception e) {           throw new runtimeexception(format("failed instantiate module factory %s screen %s",               withmodulefactory.value().getname(), screen.getname()), e);         }       }     }      if (modulefactory == null) modulefactory = no_factory;      modulefactorycache.put(screentype, modulefactory);      return modulefactory;   }    private static class noargsfactory extends modulefactory<object> {     final constructor moduleconstructor;      private noargsfactory(constructor moduleconstructor) {       this.moduleconstructor = moduleconstructor;     }      @override protected object createdaggermodule(resources resources, object ignored) {       try {         return moduleconstructor.newinstance();       } catch (instantiationexception | illegalaccessexception | invocationtargetexception e) {         throw new runtimeexception(e);       }     }   }    private static class singleargfactory extends modulefactory {     final constructor moduleconstructor;      public singleargfactory(constructor moduleconstructor) {       this.moduleconstructor = moduleconstructor;     }      @override protected object createdaggermodule(resources resources, object screen) {       try {         return moduleconstructor.newinstance(screen);       } catch (instantiationexception | illegalaccessexception | invocationtargetexception e) {         throw new runtimeexception(e);       }     }   } } 

Comments

Popular posts from this blog

android - Get AccessToken using signpost OAuth without opening a browser (Two legged Oauth) -

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: mockito -

google shop client API returns 400 bad request error while adding an item -