/*
* Copyright © 2016 TIBCO Software,Inc.All rights reserved.
* http://community.jaspersoft.com/project/jaspermobile-android
*
* Unless you have purchased a commercial license agreement from TIBCO Jaspersoft,
* the following license terms apply:
*
* This program is part of TIBCO Jaspersoft Mobile for Android.
*
* TIBCO Jaspersoft Mobile is free software:you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation,either version 3of the License,or
* (at your option)any later version.
*
* TIBCO Jaspersoft Mobile is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY;without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with TIBCO Jaspersoft Mobile for Android.If not,see
* <http://www.gnu.org/licenses/lgpl>.
*/
package com.jaspersoft.android.jaspermobile.ui.presenter;
import android.support.annotation.VisibleForTesting;
import com.jaspersoft.android.jaspermobile.domain.PageRequest;
import com.jaspersoft.android.jaspermobile.domain.ReportControlFlags;
import com.jaspersoft.android.jaspermobile.domain.ReportPage;
import com.jaspersoft.android.jaspermobile.domain.ScreenCapture;
import com.jaspersoft.android.jaspermobile.domain.SimpleSubscriber;
import com.jaspersoft.android.jaspermobile.domain.executor.PostExecutionThread;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.FlushInputControlsCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.FlushReportCachesCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportMultiPagePropertyCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportPageContentCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportShowControlsPropertyCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportTotalPagesPropertyCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.ReloadReportCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.RunReportCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.report.UpdateReportCase;
import com.jaspersoft.android.jaspermobile.domain.interactor.resource.SaveScreenCaptureCase;
import com.jaspersoft.android.jaspermobile.internal.di.PerActivity;
import com.jaspersoft.android.jaspermobile.network.RequestExceptionHandler;
import com.jaspersoft.android.jaspermobile.ui.contract.RestReportContract;
import com.jaspersoft.android.jaspermobile.ui.model.visualize.ErrorEvent;
import com.jaspersoft.android.jaspermobile.ui.model.visualize.VisualizeViewModel;
import com.jaspersoft.android.jaspermobile.ui.page.ReportPageState;
import com.jaspersoft.android.sdk.service.exception.ServiceException;
import com.jaspersoft.android.sdk.service.exception.StatusCodes;
import java.io.File;
import javax.inject.Inject;
import javax.inject.Named;
import rx.Subscriber;
import rx.Subscription;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;
/**
* @author Tom Koptel
* @since 2.3
*/
@PerActivity
public class ReportViewPresenter extends LegacyPresenter<RestReportContract.View> implements RestReportContract.Action {
private final String mReportUri;
private final PostExecutionThread mPostExecutionThread;
private final RequestExceptionHandler mExceptionHandler;
private final GetReportShowControlsPropertyCase mGetReportShowControlsPropertyCase;
private final GetReportMultiPagePropertyCase mGetReportMultiPagePropertyCase;
private final GetReportTotalPagesPropertyCase mGetReportTotalPagesPropertyCase;
private final GetReportPageContentCase mGetReportPageContentCase;
private final RunReportCase mRunReportCase;
private final UpdateReportCase mUpdateReportCase;
private final ReloadReportCase mReloadReportCase;
private final FlushReportCachesCase mFlushReportCachesCase;
private final FlushInputControlsCase mFlushInputControlsCase;
private final SaveScreenCaptureCase mSaveScreenCaptureCase;
private CompositeSubscription mCompositeSubscription;
@Inject
public ReportViewPresenter(
@Named("report_uri") String reportUri,
PostExecutionThread postExecutionThread,
RequestExceptionHandler exceptionHandler,
GetReportShowControlsPropertyCase getReportShowControlsPropertyCase,
GetReportMultiPagePropertyCase getReportMultiPagePropertyCase,
GetReportTotalPagesPropertyCase getReportTotalPagesPropertyCase,
GetReportPageContentCase getReportPageContentCase,
RunReportCase runReportCase,
UpdateReportCase updateReportCase,
ReloadReportCase reloadReportCase,
FlushReportCachesCase flushReportCachesCase,
FlushInputControlsCase flushInputControlsCase,
SaveScreenCaptureCase saveScreenCaptureCase
) {
mReportUri = reportUri;
mPostExecutionThread = postExecutionThread;
mExceptionHandler = exceptionHandler;
mGetReportShowControlsPropertyCase = getReportShowControlsPropertyCase;
mGetReportMultiPagePropertyCase = getReportMultiPagePropertyCase;
mGetReportTotalPagesPropertyCase = getReportTotalPagesPropertyCase;
mGetReportPageContentCase = getReportPageContentCase;
mRunReportCase = runReportCase;
mUpdateReportCase = updateReportCase;
mReloadReportCase = reloadReportCase;
mFlushReportCachesCase = flushReportCachesCase;
mFlushInputControlsCase = flushInputControlsCase;
mSaveScreenCaptureCase = saveScreenCaptureCase;
}
public void init() {
if (getView() == null) {
throw new NullPointerException("Please inject view before calling this method");
}
ReportPageState state = getView().getState();
if (state.isControlsPageShown() && state.getCurrentPage() != null) {
loadLastSavedPage();
loadMultiPageProperty();
loadTotalPagesProperty();
boolean hasControls = state.hasControls();
toggleFiltersAction(hasControls);
} else {
loadReportMetadata();
}
}
private void loadLastSavedPage() {
loadPageByPosition(getView().getState().getCurrentPage());
}
private void loadPageByPosition(final String position) {
PageRequest request = new PageRequest.Builder()
.setUri(mReportUri)
.setRange(position)
.build();
mGetReportPageContentCase.execute(request, new ErrorSubscriber<>(new SimpleSubscriber<ReportPage>() {
@Override
public void onStart() {
getView().showWebView(false);
getView().showPageLoader(true);
}
@Override
public void onNext(ReportPage item) {
if (item.isEmpty()) {
handleExportOutOfRange();
} else {
showPage(position, item);
}
}
@Override
public void onError(Throwable e) {
getView().showPageLoader(false);
}
}));
}
private void loadReportMetadata() {
showLoading();
mGetReportShowControlsPropertyCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<ReportControlFlags>() {
@Override
public void onNext(ReportControlFlags flags) {
getView().getState().setControlsPageShown(true);
boolean hasControls = flags.hasControls();
getView().getState().setHasControls(hasControls);
toggleFiltersAction(hasControls);
boolean needPrompt = flags.needPrompt();
resolveNeedControls(needPrompt);
}
}));
}
private void resolveNeedControls(boolean needControls) {
if (needControls) {
getView().showInitialFiltersPage();
} else {
runReport();
}
}
@Override
public void resume() {
mCompositeSubscription = new CompositeSubscription();
subscribeToVisualizeEvents();
}
@Override
public void pause() {
mCompositeSubscription.unsubscribe();
}
private void subscribeToVisualizeEvents() {
VisualizeViewModel visualize = getView().getVisualize();
listenForAuthErrorEvent(visualize);
}
private void listenForAuthErrorEvent(VisualizeViewModel visualize) {
subscribeToEvent(
visualize.visualizeEvents()
.authErrorEvent()
.observeOn(mPostExecutionThread.getScheduler())
.subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ErrorEvent>() {
@Override
public void onNext(ErrorEvent item) {
getView().hideLoading();
reloadByPosition(getView().getState().getRequestedPage());
}
}))
);
}
private void subscribeToEvent(Subscription subscription) {
mCompositeSubscription.add(subscription);
}
@Override
public void destroy() {
mGetReportShowControlsPropertyCase.unsubscribe();
mGetReportMultiPagePropertyCase.unsubscribe();
mGetReportTotalPagesPropertyCase.unsubscribe();
mGetReportPageContentCase.unsubscribe();
mRunReportCase.unsubscribe();
mUpdateReportCase.unsubscribe();
mReloadReportCase.unsubscribe();
mFlushReportCachesCase.execute(mReportUri);
mFlushInputControlsCase.execute(mReportUri);
}
@Override
public void loadPage(String pageRange) {
getView().getState().setRequestedPage(pageRange);
loadPageByPosition(pageRange);
}
@Override
public void runReport() {
showLoading();
getView().getState().setRequestedPage("1");
mRunReportCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<ReportPage>() {
@Override
public void onNext(ReportPage page) {
showPageInView("1", page);
}
}));
}
@VisibleForTesting
void loadMultiPageProperty() {
mGetReportMultiPagePropertyCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<Boolean>() {
@Override
public void onNext(Boolean multiPage) {
ReportPageState state = getView().getState();
Integer totalPages = state.getTotalPages();
if (totalPages != null) {
multiPage &= totalPages > 1;
}
togglePaginationControl(multiPage);
}
}));
}
@VisibleForTesting
void loadTotalPagesProperty() {
mGetReportTotalPagesPropertyCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<Integer>() {
@Override
public void onNext(Integer totalPages) {
ReportPageState state = getView().getState();
state.setTotalPages(totalPages);
boolean hasNoPages = (totalPages == 0);
toggleSaveAction(!hasNoPages);
boolean multiPage = (totalPages > 1);
togglePaginationControl(multiPage);
if (hasNoPages) {
showEmptyPage();
} else {
updateTotalPagesLabel(totalPages);
loadLastSavedPage();
}
}
}));
}
@Override
public void updateReport() {
showLoading();
resetTotalPagesLabel();
mUpdateReportCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<ReportPage>() {
@Override
public void onNext(ReportPage page) {
showPageInView("1", page);
}
}));
}
@Override
public void refresh() {
reloadByPosition("1");
}
@Override
public void shareReport(ScreenCapture screenCapture) {
mSaveScreenCaptureCase.execute(screenCapture, new SimpleSubscriber<File>() {
@Override
public void onStart() {
getView().showProgress();
}
@Override
public void onError(Throwable e) {
handleError(e);
}
@Override
public void onNext(File item) {
getView().hideLoading();
getView().navigateToAnnotationPage(item);
}
});
}
private void reloadByPosition(final String position) {
showLoading();
resetTotalPagesLabel();
PageRequest request = new PageRequest.Builder()
.setUri(mReportUri)
.setRange(position)
.build();
mReloadReportCase.execute(request, new ErrorSubscriber<>(new SimpleSubscriber<ReportPage>() {
@Override
public void onNext(ReportPage page) {
showPageInView(position, page);
}
}));
}
private void showPageInView(String position, ReportPage page) {
if (page.isEmpty()) {
showEmptyPage();
} else {
showPage(position, page);
loadMultiPageProperty();
loadTotalPagesProperty();
}
}
private void showPageOutOfRangeError() {
getView().showPageOutOfRangeError();
}
private void showEmptyPage() {
getView().showEmptyPageMessage();
}
private void resetTotalPagesLabel() {
getView().resetPaginationControl();
}
private void updateTotalPagesLabel(int pages) {
getView().showTotalPages(pages);
}
private void toggleSaveAction(boolean visibility) {
getView().setSaveActionVisibility(visibility);
getView().reloadMenu();
}
private void toggleFiltersAction(boolean visibility) {
getView().setFilterActionVisibility(visibility);
getView().reloadMenu();
}
private void togglePaginationControl(boolean multiPage) {
getView().showPaginationControl(multiPage);
}
private void hideLoading() {
getView().hideLoading();
}
private void showLoading() {
getView().showLoading();
}
@VisibleForTesting
void showPage(String pagePosition, ReportPage page) {
getView().hideError();
getView().showCurrentPage(Integer.valueOf(pagePosition));
getView().getState().setCurrentPage(pagePosition);
getView().showPage(new String(page.getContent()));
}
@VisibleForTesting
void handleError(Throwable error) {
if (error instanceof ServiceException) {
ServiceException serviceException = (ServiceException) error;
tryToRecoverFromSdkError(serviceException);
} else {
Timber.e(error, "Presenter received unexpected error");
showErrorMessage(error);
}
}
private void tryToRecoverFromSdkError(ServiceException serviceException) {
int errorCode = serviceException.code();
switch (errorCode) {
case StatusCodes.EXPORT_PAGE_OUT_OF_RANGE:
handleExportOutOfRange();
break;
case StatusCodes.REPORT_EXECUTION_INVALID:
reloadByPosition(getView().getState().getRequestedPage());
break;
default:
Timber.e(serviceException, "Page request operation crashed with SDK exception");
showErrorMessage(serviceException);
}
}
private void handleExportOutOfRange() {
showPageOutOfRangeError();
loadLastSavedPage();
}
private void showErrorMessage(Throwable error) {
getView().hideLoading();
getView().showError(mExceptionHandler.extractMessage(error));
}
private class ErrorSubscriber<R> extends Subscriber<R> {
private final Subscriber<R> mDelegate;
private ErrorSubscriber(Subscriber<R> delegate) {
mDelegate = delegate;
}
@Override
public void onStart() {
getView().hideError();
mDelegate.onStart();
}
@Override
public void onCompleted() {
hideLoading();
mDelegate.onCompleted();
}
@Override
public void onError(Throwable e) {
Timber.e(e, "Error on REST Report Presenter");
handleError(e);
hideLoading();
mDelegate.onError(e);
}
@Override
public void onNext(R r) {
mDelegate.onNext(r);
}
}
}