user1341300 user1341300 - 5 months ago 37
Android Question

Activity presenter will update the tablayout fragments

I have an Activity1 with a TabLayout of two Fragments (each of them with a presenter). Once I click a button on the Toolbar a new Activity2 is started (with startActivityWithResults) which contains a simple list. At the selection of one of the items in the list the Activity2 returns the selected string to the previous Activity1 (the one with the TabLayout).

Now, once onActivityResult is called in Activity1, this one will call an API (using a presenter) that will get the new results and then it should update the two fragments in the TabLayout. I'm thinking to do it with RxJava but I have no idea where to start from.

The Activity1:

public class Activity1 extends BaseActivity {

@Inject
Actvity1Presenter mPresenter;

public static Intent newIntent(Context packageContext) {
return new Intent(packageContext, Activity1.class);
}

@LayoutRes
protected int getLayoutRedIs() {
return R.layout.app_bar_activity1;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutRedIs());

FwApplication.component(this).inject(this);

mPresenter.attachView(this);

Toolbar tb = (Toolbar) findViewById(R.id.toolbar_chips);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_back_arrow);

mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
mTabLayout.addTab(mTabLayout.newTab().setText("TAB1"));
mTabLayout.addTab(mTabLayout.newTab().setText("TAB2"));
mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(),
mTabLayout.getTabCount()));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(TabLayout.Tab tab) {

}

@Override
public void onTabReselected(TabLayout.Tab tab) {

}
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_ITEM_CODE) {
if (resultCode == RESULT_OK) {
mPresenter.updateResults(data);
}

if (resultCode == RESULT_CANCELED) {

}
}
}


And the pager:

public class PagerAdapter extends FragmentPagerAdapter {

int mNumOfTabs;

public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}

@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return Fragment1.newInstance();
break;
case 1:
return Fragment2.newInstance();
break;
}
}

@Override
public int getCount() {
return mNumOfTabs;
}
}


EDIT

ActivityPresenter:

public class ActivityPresenter implements Presenter<ActivityView>,
Interactor.OnFinishedListener<Response> {

private static final String TAG = "FW.ActivityPresenter";

@Inject
QueryPreferences mQueryPreferences;

private Interactor mInteractor;
private ActivityView mView;
private NetworkService mNetworkService;
private boolean mUseCache;
private String mQuery;
private int mPage;

private PublishSubject<Response> mPublishSubject = PublishSubject.create();

Observable<Response> getObservableResults() {
return mPublishSubject;
}

@Inject
public ActivityPresenter(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}

public void onSearchQueryListener(String query, int page) {
mQuery = mQueryPreferences.getStoredQuery();
mUseCache = query.equals(mQuery);

if (!mUseCache) {
mQueryPreferences.setStoredQuery(query);
Log.d(TAG, "Query added to cache: " + query);
}

mPage = page;
mInteractor.loadResults(this, query, false, page);
}

@Override
public void onFinished(Response response) {
if (mView != null) {
mPublishSubject.onNext(response);
}
}

@Override
public void onError(Throwable throwable) {
if (mView != null) {
mView.showMessage(throwable.getMessage());
}
}

@Override
public void attachView(ActivityView mvpView) {
mView = mvpView;
}

@Override
public void detachView() {
mView = null;
mInteractor.unSubscribe();
}
}


InteractorImpl:

public class InteractorImpl implements Interactor {

private static final String TAG = "FW.InteractorImpl";

private NetworkService mNetworkService;
private Subscription mSubscription;

public InteractorImpl(NetworkService networkService) {
mNetworkService = networkService;
}

@Override
public void loadResults(final OnFinishedListener listener, String query, boolean useCache, int page) {
Observable<Response> responseObservable = (Observable<Response>)
mNetworkService.getObservable(mNetworkService.getAPI().getResponseObservable(query, page), Response.class, true, useCache);

mSubscription = responseObservable.subscribe(new Observer<Response>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
listener.onError(e);
}

@Override
public void onNext(Response response) {
listener.onFinished(response);
}
});
}

public void unSubscribe() {
if(mSubscription != null && !mSubscription.isUnsubscribed()) {
mSubscription.unsubscribe();
}
}
}


FragmentPresenter:

public class FragmentPresenter implements Presenter<FragmentView>,
Interactor.OnFinishedListener<Response> {

private static final String TAG = "FW.FragmentPres";

@Inject
QueryPreferences mQueryPreferences;

private Interactor mInteractor;
private FragmentView mView;
private NetworkService mNetworkService;

private ActivityPresenter mActvityPresenter;

@Inject
public FragmentPresenter(NetworkService networkService) {
mNetworkService = networkService;
mInteractor = new InteractorImpl(mNetworkService);
}

void attachRecipeActivityPresenter(ActivityPresenter activityPresenter) {
mActvityPresenter = activityPresenter;
mActvityPresenter.getObservableResults().subscribe(data -> showData(data));
}

private void showData(Response response) {
if (response.getResults().getModels().isEmpty() && mPage == 0) {
mView.showNoResults();
} else {
mView.showResults(response.getResults().getModels());
}
}

@Override
public void onError(Throwable throwable) {
if (mView != null) {
mView.hideProgressBar();
mView.showMessage(throwable.getMessage());
}
}

@Override
public void attachView(FragmentView mvpView) {
mView = mvpView;
}

@Override
public void detachView() {
mView = null;
mInteractor.unSubscribe();
}
}

Answer

Using Retrofit2 and RxAndroid your method will look like this:

public void updateResults(String data) {
    yourRetrofitAPI.getSomething(data)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSubscribe(() -> {
                // show your progress dialog
            })
            .subscribe(result -> {
                // pass result to your view
            }, error -> {
                // hide your progress dialog
                // get error message and send to your view
            }, () -> {
                // hide your progress dialog
            });
}

interface YourRetrofitAPI {
    @GET("/yourResource/{data}")
    Observable<String> getSomething(@Path("data") String data);
}

So, about notify your fragments, with MVP you can make presenter fragments observe a stream from activity presenter, so both fragments will be notified when you query ends.

public class ExampleUnitTest {
    @Test
    public void testSample() throws Exception {
        ActivityPresenter activityPresenter = new ActivityPresenter();

        Fragment1Presenter fragment1Presenter = new Fragment1Presenter();
        Fragment2Presenter fragment2Presenter = new Fragment2Presenter();

        fragment1Presenter.attachActivityPresenter(activityPresenter);
        fragment2Presenter.attachActivityPresenter(activityPresenter);

        Observable.range(1, 10)
                .delay(2, TimeUnit.SECONDS, Schedulers.immediate())
                .subscribe(integer -> activityPresenter.queryData("query: " + integer));
    }

    class ActivityPresenter {
        PublishSubject<String> publishSubject = PublishSubject.create();

        Observable<String> serverDataAsObservable() {
            return publishSubject.map(s -> String.format("%d - %s", System.currentTimeMillis(), s));
        }

        void queryData(String input) {
            // based on your input you should query data from server
            // and then emit those data with publish subject
            // then all subscribers will receive changes
            publishSubject.onNext(input);
        }
    }

    class Fragment1Presenter {
        private ActivityPresenter activityPresenter;

        void attachActivityPresenter(ActivityPresenter activityPresenter) {
            this.activityPresenter = activityPresenter;
            this.activityPresenter.serverDataAsObservable()
                    .subscribe(data -> showData(data));
        }

        private void showData(String data) {
            System.out.println("showing data on fragment1 with " + data);
        }
    }

    class Fragment2Presenter {
        private ActivityPresenter activityPresenter;

        void attachActivityPresenter(ActivityPresenter activityPresenter) {
            this.activityPresenter = activityPresenter;
            this.activityPresenter.serverDataAsObservable()
                    .subscribe(data -> showData(data));
        }

        private void showData(String data) {
            System.out.println("showing data on fragment2 with " + data);
        }
    }
}

Hope that it helps.

Best regards.