Implementing OAuth2 with AccountManager, Retrofit and Dagger - android

I'm trying to figure out what would be the best way to implement a Retrofit client which supports AccountManager.getAuthToken() for OAuth2 flow.
I'm following the U2020
Ideally I would like to have a simple injector along these lines
public class ExampleFragment extends InjectionFragment {
#Inject ApiDatabase database;
#Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
subscribe(database.getSomeData(), ...);
}
}
I'm considering a RequestInterceptor similar to the example
public final class ApiHeaders implements RequestInterceptor {
ApiKeyProvider apiKeyProvider;
#Inject
public ApiHeaders(ApiKeyProvider apiKeyProvider) {
this.apiKeyProvider = apiKeyProvider;
}
#Override
public void intercept(RequestFacade request) {
// How to handle exceptions from getAuthToken?
request.addHeader("Authorization", "Bearer " + apiKeyProvider.getAuthKey());
}
}
and
public class ApiKeyProvider {
AccountManager accountManager;
Activity activity;
public ApiKeyProvider(Activity activity, AccountManager accountManager) {
this.activity = activity;
this.accountManager = accountManager;
}
public String getAuthKey() throws AccountsException, IOException {
AccountManagerFuture accountManagerFuture = accountManager.getAuthTokenByFeatures(ACCOUNT_TYPE,
AUTHTOKEN_TYPE, new String[0], activity, null, null, null, null);
return accountManagerFuture.getResult().getString(KEY_AUTHTOKEN);
}
}
I'm not sure how to inject the ApiKeyProvider into the ApiHeaders class as it depends on an "ActivityModule" (lower down the dagger DAG graph).
Also not sure how to handle exceptions.
Can anyone provide a full working example?

This ended up being a little lengthy.
This GIST hopefully encompasses all the relevant files

Related

How to implement Cursor Loader in MVP pattern?

I'm trying to create Login activity in app based on MVP pattern. User input will be sent to the database using Cursor Loader.
1# Is it the correct way to pass user input from view to model, using presenter(and there's validation included) and in model provide it using other db provider class?
public class LoginActivityModel extends AppCompatActivity implements LoginActivityMVP.Model, LoaderManager.LoaderCallbacks<Cursor> {
private ApplicationModule applicationModule;
#Override
public void sendUserToDb(String firstName, String secondName, Uri currentUserUri) {
ContentValues values = new ContentValues();
String date = new SimpleDateFormat("dd-MM-yyyy").format(new Date());
values.put(UserEntry.COLUMN_USERNAME, firstName);
values.put(UserEntry.COLUMN_PASSWORD, secondName);
values.put(UserEntry.COLUMN_DATE, date);
if (currentUserUri != null) {
getSupportLoaderManager().initLoader(0, null, this);
}
Uri newUri = applicationModule.provideContext().getContentResolver().insert(UserEntry.CONTENT_URI, values);
if (newUri == null) {
Toast.makeText(applicationModule.provideContext(), "failed",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(applicationModule.provideContext(), "succeed",
Toast.LENGTH_SHORT).show();
}
}
2a# If yes, how can I inject context from my view? I tried, as you can see above, to get context injected in root appliaction module but it doesn't work. I've got null context from App. Any other way that I found here, on stackoverflow didn't work.
#Module
public class ApplicationModule {
private Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
public Context provideContext() {
return application;
}
}
Also, my App
public class App extends Application {
private ApplicationComponent component;
public LoginActivity login;
#Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.loginActivityModule(new LoginActivityModule())
.build();
}
public ApplicationComponent getComponent() {
return component;
}
}
2b# If no, how should I implement Cursor Loader into MVP pattern based appliaction? Or maybe is it a bad practice?

Unit test using Mockito- Make mock of Abstract, void method

I am trying MVP pattern with TDD.
I have the following contract for Model, View And Presenter
Contract Class
interface GithubContract {
interface View {
void displayUsers(List<GurkhaComboDTO> userList);
}
interface Model {
void getUsersAndPromptPresenter(String userName, Presenter presenter);
}
interface Presenter {
void searchUsers(String userName);
void loadUsers(List<GithubUserDTO> userList);
}
}
I am trying to unit test the presenter logic like this :
Test Class
#RunWith(MockitoJUnitRunner.class)
public class GithubPresenterWithMockitoTest {
#Mock
GithubContract.Model mockedModel;
#Test
public void shouldDisplayUsersToScreen() {
//given
final GithubContract.View view = new MockView(); // I have created the mock myself for the view this time.
final GithubContract.Presenter presenter = new GithubPresenter(view, mockedModel);
***********************************************************
// I do not know what to write here
****************************************************
presenter.searchUsers("");
Assert.assertEquals(true, ((MockView) (view)).enoughItems);
}
}
My MockView / VIEW class looks like this :
This is -> Mock class
class MockView implements GithubContract.View {
boolean enoughItems = false;
#Override
public void displayUsers(List<GurkhaComboDTO> userList) {
enoughItems = true;
}
}
My PRESENTER implementation of contract is like this ..
This is -> Real Class
class GithubPresenter implements GithubContract.Presenter {
private GithubContract.View view;
private GithubContract.Model model;
GithubPresenter(GithubContract.View view, GithubContract.Model model) {
this.view = view;
this.model = model;
}
#Override
public void searchUsers(String userName) {
model.getUsersAndPromptPresenter(userName, this);
}
#Override
public void loadUsers(List<GithubUserDTO> data) {
if (data != null) {
if (!data.isEmpty()) {
view.displayUsers(users);
}
}
}
I have the MODEL class Implementation like this :
This is -> Real Class
public class GithubModel implements Model {
#Inject
GithubAPIService apiService;
private Call<GithubUserListDTO> userListCall;
private Context context;
GithubModel(Context context) {
this.context = context;
apiService = Util.getAPIService(); // I am using dagger, retrofit and okhttp3 with GSON to get Objects directly from network call
}
#Override
public void getUsersAndPromptPresenter(final String userName, final GithubContract.Presenter presenter) {
userListCall = apiService.searchGitHubUsers(userName);
if(Util.isInternetConnected(context)) {
userListCall.enqueue(new Callback<GithubUserListDTO>() {
#Override
public void onResponse(Call<GithubUserListDTO> call, Response<GithubUserListDTO> response) {
try {
presenter.loadUsers(response.body().getList());
} catch (Exception ignored) {
Util.log(ignored.getMessage());
}
}
#Override
public void onFailure(Call<GithubUserListDTO> call, Throwable t) {
}
});
}else {
Util.log("No Internet");
}
}
}
Now the real problem part:
I was successfully able to test the presenter with the mock of GithubContract.Model myself, But I want to use Mockito to mock the Model but as my getUsersAndPromptPresenter() method is abstract, returns void, takes parameters and calls back to presenter from an Inner class inside the method.
How can I mock my Model? If I need to bring some change in architecture in order to be able to make it testable, then please suggest it.
You shouldn't pass presenter to Model, Model and Presenter shouldn't be tightly coupled because it prevents model classes from being reusable. Instead provide succesfull and error callbacks(or a composite object that contains both these callbacks). And then you will be able to capture that callback with mockito and call the required one. Also it's very common today to use RxJava, it makes it easier to mock Model classes.
And here is a general good practice: you should avoid to use And/Or words in method names because it indicates that the method is doing more than one thing which is bad

Dagger2 singleton annotation not working

So, a bit of context. I'm using Dagger2, Retrofit and RxAndroid and structuring my app using an MVP architecture.
For now, all I'm doing is making a network request to the API a retrieving some information as soon as my main activity starts. I'm trying to persist my presenters through configuration changes to avoid making a new http request every time I rotate my screen.
MainActivity.java
public class MainActivity extends AppCompatActivity implements ForecastView {
#Inject
Presenter forecastPresenter;
private TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.weather);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initializeDependencies();
initializePresenter();
}
private void initializeDependencies() {
DaggerWeatherApiComponent.builder()
.build().inject(this);
}
private void initializePresenter() {
forecastPresenter.attachView(this);
forecastPresenter.onCreate();
}
WeatherApiComponent.java
#Component(modules = {EndpointsModule.class})
#Singleton
public interface WeatherApiComponent {
void inject(MainActivity context);
}
EndpointsModule.java
#Module #Singleton
public class EndpointsModule {
#Provides
#Singleton
WeatherEndpoints provideEndpoints() {
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient())
.baseUrl("http://api.openweathermap.org/data/2.5/")
.build();
return retrofit.create(WeatherEndpoints.class);
}
#Provides
#Singleton
Repository providesRepository(RestRepository repository) {
return repository;
}
#Provides
#Singleton
Presenter providesPresenter(ForecastPresenter presenter) {
return presenter;
}
}
RestRespository
public class RestRepository implements Repository {
private WeatherEndpoints endpoints;
static final String API_KEY = "xxxxxxxxxxxxxxxxxxxxx";
#Inject
public RestRepository(WeatherEndpoints endpoints) {
this.endpoints = endpoints;
}
public Observable<Current> getCurrentWeather(String cityName) {
return endpoints.getCurrent(cityName, API_KEY);
}
public Observable<com.feresr.rxweather.models.List> getForecast(String cityName) {
return endpoints.getForecast(cityName, API_KEY).flatMap(new Func1<FiveDays, Observable<com.feresr.rxweather.models.List>>() {
#Override
public Observable<com.feresr.rxweather.models.List> call(FiveDays fiveDays) {
return Observable.from(fiveDays.getList());
}
});
}
}
ForecastPresenter.java
public class ForecastPresenter implements Presenter {
private GetForecastUseCase useCase;
private Subscription forecastSubscription;
private ArrayList<List> lists;
private ForecastView forecastView;
#Inject
public ForecastPresenter(GetForecastUseCase forecastUseCase) {
this.useCase = forecastUseCase;
lists = new ArrayList<>();
}
#Override
public void onStop() {
if (forecastSubscription.isUnsubscribed()) {
forecastSubscription.unsubscribe();
}
}
#Override
public void attachView(View v) {
forecastView = (ForecastView) v;
}
#Override
public void onCreate() {
if (lists.isEmpty()) {
forecastSubscription = useCase.execute().subscribe(new Action1<List>() {
#Override
public void call(List list) {
lists.add(list);
forecastView.addForecast(list.getWeather().get(0).getMain());
}
});
} else {
forecastView.addForecast(lists.get(0).toString());
}
}
The constructor on this class (presenter) keeps calling itself as I rotate my Acitivity. I've annotated with #Singleton most of my classes. I don't know what else to do.
EDIT: Note that I haven't gotten into dagger SCOPES just yet, for now I don't care if this singleton presenter lives as long as my app. I'll fix that later.
It looks like you're recreating the Dagger component every time MainActivity.onCreate(Bundle) is called, and the activity is reinstantiated when you rotate the screen.
Like other scopes, #Singleton means there will be one instance of the object for the lifetime of the component, not for the lifetime of the JVM. You typically have to make sure there is only one instance of the #Singleton component yourself, usually by keeping it in a field in your Application.
You create a new dagger component every time here:
private void initializeDependencies() {
DaggerWeatherApiComponent.builder()
.build().inject(this);
}
A scoped dependency exists as ONE instance PER component.
If you create a new component, it will have its own scope, and it will create its own instance.
You should either invest in Mortar scopes to preserve your component, or you should have some sort of "cache" in your Application instance.

Dagger Inject different Dependency to IntentService in prod & test

Is it possible to inject different object through dagger into android.app.IntentService depending if it is a test or production?
this is mainly the code (simplified) which injects the WebRequest Class into the Service.
public class SomeService extends android.app.IntentService {
#Inject
WebReqeust mWebRequest;
public SomeService(String name) {
super(name);
MainApplication.getInstance().inject(this);
}
#Override
protected void onHandleIntent(Intent intent) {
String json = mWebRequest.getHttpString(url);
JSONObject o = new JSONObject(json);
DBHelper.insert(o);
}
}
#Module(injects = { SomeService.class })
public class WebRequestModule {
#Provides
WebRequest provideWebRequest() {
return new WebRequest();
}
}
public class Modules {
public static Object[] list() {
return new Object[] {
new WebRequestModule()
};
}
}
public class MainApplication extends Application {
private ObjectGraph mOjectGraph;
private static MainApplication sInstance;
#Override
public void onCreate() {
sInstance = this;
mOjectGraph = ObjectGraph.create(Modules.list());
}
public void inject(Object dependent) {
mOjectGraph.inject(dependent);
}
public void addToGraph(Object module) {
mOjectGraph.plus(module);
}
}
I would like to write a test which mocks the http response.
I've started with a new Module
#Module(
injects = SomeService.class,
overrides = true
)
final class MockTestModule {
#Provides
WebRequest provideWebRequest() {
WebRequest webRequest = mock(WebRequest.class);
when(webRequest.getJSONObjectResponse(contains("/register/"))).thenReturn(
new JSONObject(FileHelper.loadJSONFromAssets(this.getClass(),
"mock_register.json")));
when(webRequest.getJSONObjectResponse(contains("/register_validate/"))).thenReturn(
new JSONObject(FileHelper.loadJSONFromAssets(this.getClass(),
"mock_register_validate.json")));
return webRequest;
}
}
And in the test i tried the following
public class RegisterTest extends AndroidTestCase {
protected void setUp() throws Exception {
MainApplication.getInstance().addToGraph(new MockTestModule());
super.setUp();
}
public void test_theActuallTest() {
Registration.registerUser("email#email.com"); // this will start the service
wait_hack(); // This makes the test wait for the reposen form the intentservice, works fine
DBHelper.isUserRegisterd("email#email.com"));
}
}
The test is executed successfull (remember, the code is simplyfied and might not compile, just should represent the idea).
However, it still uses the "real" WebRequest Impl., not the Mocked one. I see it in the logs, the proxy and of ourse on the server ...
I did this with RoboGuice in a very similar way and it was working.
But somehow i am not able to get this done with dagger.
(I'm currently evaluating DI Frameworks and this is a "must have")
The plus method actual returns the new graph. It doesn't override the original graph. That being said to accomplish what you want you can simply do this.
public class MainApplication extends Application {
...
// Mostly used for testing
public void addToGraph(Object module) {
mObjectGraph = mOjectGraph.plus(module);
}
}
This takes the original graph and pluses it with your new module and then simply assigns the new graph to your mObjectGraph reference.

Dagger + Otto architecture guidance

I am trying to develop an app with a somewhat modular architecture (nobody knows about no one, but everybody still can communicate). I will begin with an example:
Inside the LoginFragment:
#OnClick
void login() {
bus.post(new AuthorizeEvent(email, password)); // bus is a Bus from Otto
}
#Subscribe
public void onAuthorizedEvent(AuthEvent event) {
// ... user was authorized
}
The Authenticator catches this event and then posts back:
#Subscribe
public void onAuthorizeEvent(AuthorizeEvent event) {
// ... login
bus.post(new AuthEvent(user));
}
Authenticator depends on a lot of stuff through Dagger:
#Inject
public Authenticator(ApiService apiService, Context context, Bus uiBus, Scheduler ioScheduler,
Scheduler uiScheduler) {
this.apiService = apiService;
this.context = context;
this.uiBus = uiBus;
this.ioScheduler = ioScheduler;
this.uiScheduler = uiScheduler;
uiBus.register(this);
}
which is provided by the AuthModule:
#Module(
complete = false,
library = true,
includes = {
ApiModule.class,
ApplicationModule.class
}
)
public class AuthModule {
#Provides
#Singleton
Authenticator provideAuthenticator(ApiService apiService, #ForApplication Context context,
#UIBus Bus uiBus, #IOScheduler Scheduler ioScheduler,
#UIScheduler Scheduler uiScheduler) {
return new Authenticator(apiService, context, uiBus, ioScheduler, uiScheduler);
}
}
My Application class:
public class AbsApplication extends Application {
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(getModules());
}
public Object[] getModules() {
return new Object[]{
new ApplicationModule(this),
new ApiModule(),
new AuthModule()
};
}
public void inject(Object object) {
graph.inject(object);
}
}
The question is - where do I instantiate (#Inject) the Authenticator? I don't use it directly in any of my classes. Can it be a field in the AbsApplication class? Should I even use Dagger for the Authenticator? Am I not using Dagger's modules properly?
I know that I can #Inject Authenticator inside the LoginFragment, but I am exploring new patterns. Bear with me, please.

Categories

Resources