square / mortar

A simple library that makes it easy to pair thin views with dedicated controllers, isolated from most of the vagaries of the Activity life cycle.
Apache License 2.0
2.16k stars 155 forks source link

Please add to sample usage of resources and intent #45

Open lexer opened 10 years ago

lexer commented 10 years ago

It's unclear what is the best way to obtain resources (strings drawables etc) and activity intent. Can add this to sample please.

imminent commented 10 years ago

My approach is to pass resources through the Module that defines the Screen's scope:

@dagger.Module(injects = ScreenView.class, //
    addsTo = MainFlow.Module.class, //
)
/* package */static final class Module {

  @Provides @Named("title") String provideTitle(Activity activity) {
    return activity.getString(R.string.title);
  }  
}

I resolve the Activity dependency though the MainFlow.Module, since the Activity creates it, it can pass itself to the constructor.

imminent commented 10 years ago

I should add, that then allows you to provide them in the @Inject constructor of the Presenter. My original attempt, and an alternative option, is to use the View (in Presenter.onLoad()): getView().getResources().getString(R.string.title). You'd notice that that would happen on every load, so that led me to storing the value in the Bundle in Presenter.onSave() and reuse it. That felt silly, and then I realized I already had the Activity dependency.

LukeStClair commented 10 years ago

Any reason not to just define a provider for resources at the application module level, then inject the resources object?

ghost commented 10 years ago

@technicalcycling plays hell with unit testing of presenters, and in particular seems sometimes to lead to battles between Robolectric and Mockito.

@imminent Making it possible to inject the Activity seems very dangerous. We tend to make our Application instance available via injection, but even then rarely use it directly. I'll typically do something like:

class MyScreen implements Blueprint {
  interface Supplies {
    CharSequence title();
    CharSequence moarText();
  }

  //...

  @Singleton Presenter extends ViewPresenter<MyView> {
    final Supplies supplies;
    @Inject Presenter(Supplies supplies) {
      this.supplies = supplies;
    }
  }

  @dagger.Module class MyModule() {
    Supplies provideSupplies(final Resources resources) {
      return new Supplies() {
        final CharSequence title = resources.getString(R.string.title);
        final CharSequence moarText = resources.getString(R.string.moarText);

        CharSequence title() { return title; }
        CharSequence moarText() { return moarText; }
      };
    }
  }
}

And then I wish for Kotlin to reduce the boilerplate.

Leaving this open as a reminder to document or sample this somewhere.

ghost commented 10 years ago

@lexer to get at the Activity intent I think I'd do something like:

protected void onCreate(Bundle savedState) {
  super.onCreate(savedState);

  MortarScope appScope = Mortar.getScope(getApplication());
  activityScope = Mortar.requireActivityScope(appScope, new ActivityBlueprint(this));
  activityScope.getObjectGraph().inject(this);
  activityScope.onCreate(savedState);
}

class ActivityBlueprint implements mortar.Blueprint {
  final Intent activityIntent;
  final String name;    

  ActivityBlueprint(Activity activity) {
    name = activity.getClass().getName() + "-task-" + activity.getTaskId();
    activityIntent = activity.getIntent();
  }

  @Override public String getMortarScopeName() {
    return SquareActivity.this.getMortarScopeName();
  }

  @Override public Object getDaggerModule() {
    return new Module();
  }

  @dagger.Module class Module {
    @Provides Intent provideActivityIntent() {
      return activityIntent;
    }
  }  
}

Or better yet make the ActivityModule provide some actual model objects described by the intent rather than the raw intent itself..

imminent commented 10 years ago

Is it dangerous if the ObjectGraph that injects the Activity has the same lifecycle as the Activity?

Dandré Allison (323) 823-4456 KeepandShare.com http://www.keepandshare.com

          Schedule & Share. Simply. Securely.

On Thu, Mar 27, 2014 at 9:52 AM, RayFromSquare notifications@github.comwrote:

@technicalcycling https://github.com/technicalcycling plays hell with unit testing of presenters, and in particular seems sometimes to lead to battles between Robolectric and Mockito.

@imminent https://github.com/imminent Making it possible to inject the Activity seems very dangerous. We tend to make our Application instance available via injection, but even then rarely use it directly. I'll typically do something like:

class MyScreen implements Blueprint { interface Supplies { CharSequence title(); CharSequence moarText(); }

//...

@Singleton Presenter extends ViewPresenter { final Supplies supplies; @Inject Presenter(Supplies supplies) { this.supplies = supplies; } }

@dagger.Module class MyModule() { Supplies provideSupplies(final Resources resources) { return new Supplies() { final CharSequence title = resources.getString(R.string.title); final CharSequence moarText = resources.getString(R.string.moarText);

    CharSequence title() { return title; }
    CharSequence moarText() { return moarText; }
  };
}

} }

And then I wish for Kotlin to reduce the boilerplate.

Leaving this open as a reminder to document or sample this somewhere.

Reply to this email directly or view it on GitHubhttps://github.com/square/mortar/issues/45#issuecomment-38829991 .

ghost commented 10 years ago

@imminent it's standard practice. Updated the snippet above to demonstrate.