/* * 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.ReportControlFlags; import com.jaspersoft.android.jaspermobile.domain.ScreenCapture; import com.jaspersoft.android.jaspermobile.domain.SimpleSubscriber; import com.jaspersoft.android.jaspermobile.domain.VisualizeTemplate; import com.jaspersoft.android.jaspermobile.domain.executor.PostExecutionThread; import com.jaspersoft.android.jaspermobile.domain.interactor.profile.AuthorizeSessionUseCase; import com.jaspersoft.android.jaspermobile.domain.interactor.report.FlushInputControlsCase; import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportMetadataCase; import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetReportShowControlsPropertyCase; import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetVisualizeExecOptionsCase; import com.jaspersoft.android.jaspermobile.domain.interactor.report.GetVisualizeTemplateCase; 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.VisualizeReportContract; import com.jaspersoft.android.jaspermobile.ui.model.visualize.ErrorEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.ExecutionReferenceClickEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.ExternalReferenceClickEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.LoadCompleteEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.MultiPageLoadEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.PageLoadCompleteEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.PageLoadErrorEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.ReportCompleteEvent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.VisualizeComponent; import com.jaspersoft.android.jaspermobile.ui.model.visualize.VisualizeExecOptions; import com.jaspersoft.android.jaspermobile.ui.model.visualize.VisualizeViewModel; import com.jaspersoft.android.jaspermobile.ui.model.visualize.WebViewErrorEvent; import com.jaspersoft.android.jaspermobile.ui.page.ReportPageState; import com.jaspersoft.android.sdk.client.oxm.resource.ResourceLookup; import java.io.File; import java.util.Collections; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import rx.Subscription; import rx.subscriptions.CompositeSubscription; import timber.log.Timber; /** * @author Tom Koptel * @since 2.3 */ @PerActivity public class ReportVisualizePresenter extends LegacyPresenter<VisualizeReportContract.View> implements VisualizeReportContract.Action { private final double mScreenDiagonal; private final String mReportUri; private final PostExecutionThread mPostExecutionThread; private final RequestExceptionHandler mRequestExceptionHandler; private final GetReportShowControlsPropertyCase mGetReportShowControlsPropertyCase; private final GetVisualizeTemplateCase mGetVisualizeTemplateCase; private final GetVisualizeExecOptionsCase mGetVisualizeExecOptionsCase; private final FlushInputControlsCase mFlushInputControlsCase; private final GetReportMetadataCase mGetReportMetadataCase; private final AuthorizeSessionUseCase mAuthorizeSessionUseCase; private final SaveScreenCaptureCase mSaveScreenCaptureCase; private CompositeSubscription mCompositeSubscription; @Inject public ReportVisualizePresenter( @Named("screen_diagonal") Double screenDiagonal, @Named("report_uri") String reportUri, PostExecutionThread postExecutionThread, RequestExceptionHandler requestExceptionHandler, GetReportShowControlsPropertyCase getReportShowControlsPropertyCase, GetVisualizeTemplateCase getVisualizeTemplateCase, GetVisualizeExecOptionsCase getVisualizeExecOptionsCase, FlushInputControlsCase flushInputControlsCase, GetReportMetadataCase getReportMetadataCase, AuthorizeSessionUseCase authorizeSessionUseCase, SaveScreenCaptureCase saveScreenCaptureCase) { mScreenDiagonal = screenDiagonal; mReportUri = reportUri; mPostExecutionThread = postExecutionThread; mRequestExceptionHandler = requestExceptionHandler; mGetReportShowControlsPropertyCase = getReportShowControlsPropertyCase; mGetVisualizeTemplateCase = getVisualizeTemplateCase; mGetVisualizeExecOptionsCase = getVisualizeExecOptionsCase; mFlushInputControlsCase = flushInputControlsCase; mGetReportMetadataCase = getReportMetadataCase; mAuthorizeSessionUseCase = authorizeSessionUseCase; mSaveScreenCaptureCase = saveScreenCaptureCase; } public void init() { if (getView() == null) { throw new NullPointerException("Please inject view before calling this method"); } if (getView().getState().isControlsPageShown()) { boolean hasControls = getView().getState().hasControls(); toggleFiltersAction(hasControls); } else { loadControls(); } } private void loadControls() { getView().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); } })); } @Override public void runReport() { loadVisualizeTemplate(); } @Override public void loadPage(String pageRange) { getView().getVisualize().loadPage(pageRange); } @Override public void updateReport() { mGetVisualizeExecOptionsCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<VisualizeExecOptions.Builder>() { @Override public void onNext(VisualizeExecOptions.Builder optionsBuilder) { // TODO fix nasty hack of passing view model to domain VisualizeExecOptions options = optionsBuilder.build(); getView().resetZoom(); getView().getVisualize().update(options.getParams()); } })); } @Override public void refresh() { getView().showLoading(); getView().showWebView(false); getView().showPagination(false); getView().showReloadButton(false); getView().resetPaginationControl(); getView().setPaginationCurrentPage(1); ReportPageState state = getView().getState(); state.setCurrentPage("1"); if (state.isSessionExpired()) { reloadVisualize(); } else { refreshVisualize(); } } @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 reloadVisualize() { mAuthorizeSessionUseCase.execute(new ErrorSubscriber<>(new SimpleSubscriber<Void>() { @Override public void onCompleted() { getView().getState().setSessionExpired(false); runReport(); } })); } private void refreshVisualize() { getView().getVisualize().refresh(); getView().resetZoom(); } private void resolveNeedControls(boolean needControls) { if (needControls) { getView().showInitialFiltersPage(); } else { loadVisualizeTemplate(); } } @VisibleForTesting void loadVisualizeTemplate() { double scale = mScreenDiagonal / 10.1; Map<String, Double> clientParams = Collections.singletonMap("initial_scale", scale); mGetVisualizeTemplateCase.execute(clientParams, new ErrorSubscriber<>( new SimpleSubscriber<VisualizeTemplate>() { @Override public void onNext(VisualizeTemplate template) { getView().loadTemplateInView(template); } })); } private void runReportOnVisualize() { mGetVisualizeExecOptionsCase.execute(mReportUri, new ErrorSubscriber<>(new SimpleSubscriber<VisualizeExecOptions.Builder>() { @Override public void onNext(VisualizeExecOptions.Builder builder) { VisualizeExecOptions options = builder.setDiagonal(mScreenDiagonal) .build(); getView().getVisualize().run(options); } })); } @Override public void resume() { mCompositeSubscription = new CompositeSubscription(); subscribeToVisualizeEvents(); subscribeToWebViewEvents(); } @Override public void pause() { mCompositeSubscription.unsubscribe(); } @Override public void destroy() { mGetReportShowControlsPropertyCase.unsubscribe(); mGetVisualizeTemplateCase.unsubscribe(); mGetVisualizeExecOptionsCase.unsubscribe(); mAuthorizeSessionUseCase.unsubscribe(); mFlushInputControlsCase.execute(mReportUri); } private void subscribeToWebViewEvents() { VisualizeViewModel visualize = getView().getVisualize(); listenForProgressChanges(visualize); listenForReceivedError(visualize); listenForSessionExpiration(visualize); } private void listenForProgressChanges(VisualizeViewModel visualize) { subscribeToEvent( visualize.webViewEvents() .progressChangedEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<Integer>() { @Override public void onNext(Integer progress) { getView().updateDeterminateProgress(progress); } })) ); } private void listenForReceivedError(VisualizeViewModel visualize) { subscribeToEvent( visualize.webViewEvents() .receivedErrorEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<WebViewErrorEvent>() { @Override public void onNext(WebViewErrorEvent event) { getView().hideLoading(); getView().showWebView(false); getView().showError("title" + "\n" + "message"); } })) ); } private void listenForSessionExpiration(VisualizeViewModel visualize) { subscribeToEvent( visualize.webViewEvents() .sessionExpiredEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<Void>() { @Override public void onNext(Void item) { getView().handleSessionExpiration(); } })) ); } private void subscribeToVisualizeEvents() { VisualizeViewModel visualize = getView().getVisualize(); listenForLoadStartEvent(visualize); listenScriptLoadedEvent(visualize); listenForLoadCompleteEvent(visualize); listenForLoadErrorEvent(visualize); listenForReportCompleteEvent(visualize); listenForPageLoadCompleteEvent(visualize); listenForPageLoadErrorEvent(visualize); listenForMultiPageLoadEvent(visualize); listenForExternalPageEvent(visualize); listenForExecutionEvent(visualize); listenForWindowErrorEvent(visualize); listenForAuthErrorEvent(visualize); } private void listenForLoadStartEvent(VisualizeComponent visualize) { subscribeToEvent( visualize.visualizeEvents() .loadStartEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<Void>() { @Override public void onNext(Void item) { getView().showLoading(); getView().showWebView(false); getView().resetPaginationControl(); } })) ); } private void listenScriptLoadedEvent(VisualizeComponent visualize) { subscribeToEvent( visualize.visualizeEvents() .scriptLoadedEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<Void>() { @Override public void onNext(Void item) { runReportOnVisualize(); } })) ); } private void listenForLoadCompleteEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .loadCompleteEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<LoadCompleteEvent>() { @Override public void onNext(LoadCompleteEvent completeEvent) { getView().hideLoading(); getView().showWebView(true); } })) ); } private void listenForLoadErrorEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .loadErrorEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ErrorEvent>() { @Override public void onNext(ErrorEvent errorEvent) { getView().hideLoading(); if (errorEvent.isEmptyPage()) { getView().showEmptyPageMessage(); getView().showPagination(false); } else { String errorMessage = errorEvent.getErrorMessage(); getView().showError(errorMessage); } } })) ); } private void listenForReportCompleteEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .reportCompleteEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ReportCompleteEvent>() { @Override public void onNext(ReportCompleteEvent event) { int totalPages = event.getTotalPages(); boolean hasContent = totalPages > 0; toggleSaveAction(hasContent); if (hasContent) { getView().hideEmptyPageMessage(); boolean multiPage = totalPages > 1; getView().showPagination(multiPage); if (multiPage) { getView().setPaginationTotalPages(totalPages); } } else { getView().showPagination(false); getView().showEmptyPageMessage(); } } })) ); } private void listenForPageLoadCompleteEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .pageLoadCompleteEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<PageLoadCompleteEvent>() { @Override public void onNext(PageLoadCompleteEvent event) { getView().showWebView(true); getView().hideError(); getView().setPaginationEnabled(true); getView().setPaginationCurrentPage(event.getPage()); } })) ); } private void listenForPageLoadErrorEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .pageLoadErrorEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<PageLoadErrorEvent>() { @Override public void onNext(PageLoadErrorEvent event) { getView().setPaginationEnabled(true); getView().setPaginationCurrentPage(event.getPage()); getView().showWebView(false); getView().showError(event.getErrorMessage()); } })) ); } private void listenForMultiPageLoadEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .multiPageLoadEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<MultiPageLoadEvent>() { @Override public void onNext(MultiPageLoadEvent event) { boolean needToShowPagination = event.isMultiPage() && getView().getPaginationTotalPages() > 1; getView().showPagination(needToShowPagination); } })) ); } private void listenForExternalPageEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .externalReferenceClickEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ExternalReferenceClickEvent>() { @Override public void onNext(ExternalReferenceClickEvent event) { getView().showExternalLink(event.getExternalReference()); } })) ); } private void listenForExecutionEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .executionReferenceClickEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ExecutionReferenceClickEvent>() { @Override public void onNext(ExecutionReferenceClickEvent event) { loadReportMetadata(event.getReportData()); } })) ); } private void loadReportMetadata(String reportData) { getView().showLoading(); mGetReportMetadataCase.execute(reportData, new ErrorSubscriber<>(new SimpleSubscriber<ResourceLookup>() { @Override public void onCompleted() { getView().hideLoading(); } @Override public void onNext(ResourceLookup lookup) { getView().executeReport(lookup); } })); } private void listenForWindowErrorEvent(VisualizeViewModel visualize) { subscribeToEvent( visualize.visualizeEvents() .windowErrorEvent() .observeOn(mPostExecutionThread.getScheduler()) .subscribe(new ErrorSubscriber<>(new SimpleSubscriber<ErrorEvent>() { @Override public void onNext(ErrorEvent event) { if (!event.isEmptyPage()) { getView().showError(event.getErrorMessage()); } } })) ); } 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(); getView().getState().setSessionExpired(true); getView().handleSessionExpiration(); } })) ); } @VisibleForTesting void handleError(Throwable error) { Timber.e(error, "Presenter received unexpected error"); getView().hideLoading(); getView().showError(mRequestExceptionHandler.extractMessage(error)); } private void toggleFiltersAction(boolean visibility) { getView().showFilterAction(visibility); getView().reloadMenu(); } private void toggleSaveAction(boolean visibility) { getView().showSaveAction(visibility); getView().reloadMenu(); } private void subscribeToEvent(Subscription subscription) { mCompositeSubscription.add(subscription); } private final class ErrorSubscriber<R> extends SimpleSubscriber<R> { private final SimpleSubscriber<R> mDelegate; private ErrorSubscriber(SimpleSubscriber<R> delegate) { mDelegate = delegate; } @Override public void onError(Throwable e) { getView().hideLoading(); handleError(e); mDelegate.onError(e); } @Override public void onCompleted() { getView().hideLoading(); mDelegate.onCompleted(); } @Override public void onNext(R item) { mDelegate.onNext(item); } } }