/* * 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.activities.repository.fragment; import android.os.Bundle; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.View; import android.widget.TextView; import com.jaspersoft.android.jaspermobile.Analytics; import com.jaspersoft.android.jaspermobile.R; import com.jaspersoft.android.jaspermobile.activities.info.ResourceInfoActivity_; import com.jaspersoft.android.jaspermobile.domain.JasperServer; import com.jaspersoft.android.jaspermobile.domain.SearchResult; import com.jaspersoft.android.jaspermobile.domain.interactor.resource.GetRootFoldersCase; import com.jaspersoft.android.jaspermobile.domain.interactor.resource.SearchResourcesCase; import com.jaspersoft.android.jaspermobile.network.RequestExceptionHandler; import com.jaspersoft.android.jaspermobile.ui.view.fragment.BaseFragment; import com.jaspersoft.android.jaspermobile.util.DefaultPrefHelper; import com.jaspersoft.android.jaspermobile.util.FavoritesHelper; import com.jaspersoft.android.jaspermobile.util.ResourceOpener; import com.jaspersoft.android.jaspermobile.util.ViewType; import com.jaspersoft.android.jaspermobile.util.filtering.RepositoryResourceFilter; import com.jaspersoft.android.jaspermobile.util.resource.JasperResource; import com.jaspersoft.android.jaspermobile.util.resource.viewbinder.JasperResourceAdapter; import com.jaspersoft.android.jaspermobile.util.resource.viewbinder.JasperResourceConverter; import com.jaspersoft.android.jaspermobile.widget.JasperRecyclerView; import com.jaspersoft.android.sdk.client.oxm.report.FolderDataResponse; import com.jaspersoft.android.sdk.client.oxm.resource.ResourceLookup; import com.jaspersoft.android.sdk.client.oxm.resource.ResourceLookupSearchCriteria; import com.nostra13.universalimageloader.core.ImageLoader; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EFragment; import org.androidannotations.annotations.FragmentArg; import org.androidannotations.annotations.InstanceState; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import javax.inject.Inject; import javax.inject.Named; import rx.Subscriber; import timber.log.Timber; /** * @author Tom Koptel * @since 1.9 */ @EFragment(R.layout.fragment_refreshable_resource) public class RepositoryFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener { public static final String TAG = RepositoryFragment.class.getSimpleName(); public static final String ROOT_URI = "/"; // Loader actions private static final int LOAD_FROM_CACHE = 1; private static final int LOAD_FROM_NETWORK = 2; protected JasperRecyclerView listView; protected SwipeRefreshLayout swipeRefreshLayout; protected TextView emptyText; @Inject protected Analytics analytics; @Inject @Named("LIMIT") protected int mLimit; @Inject @Named("THRESHOLD") protected int mTreshold; @Inject protected SearchResourcesCase mSearchResourcesCase; @Inject protected GetRootFoldersCase mGetRootFoldersCase; @Inject protected JasperResourceConverter jasperResourceConverter; @Inject protected FavoritesHelper favoritesHelper; @Inject protected JasperServer mServer; @InstanceState @FragmentArg String prefTag; @InstanceState @FragmentArg protected String resourceLabel; @InstanceState @FragmentArg protected String resourceUri; @InstanceState @FragmentArg protected String query; @InstanceState @FragmentArg protected boolean recursive; @FragmentArg protected ViewType viewType; @InstanceState protected boolean mLoading; @InstanceState protected boolean mHasNextPage; @Bean RepositoryResourceFilter repositoryResourceFilter; @Bean protected DefaultPrefHelper prefHelper; @Bean protected ResourceOpener resourceOpener; private JasperResourceAdapter mAdapter; private HashMap<String, ResourceLookup> mResourceLookupHashMap; protected ResourceLookupSearchCriteria mSearchCriteria; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getBaseActivityComponent().inject(this); mResourceLookupHashMap = new HashMap<>(); mSearchCriteria = new ResourceLookupSearchCriteria(); mSearchCriteria.setForceFullPage(true); mSearchCriteria.setLimit(mLimit); mSearchCriteria.setRecursive(recursive); mSearchCriteria.setTypes(repositoryResourceFilter.getCurrent().getValues()); mSearchCriteria.setFolderUri(TextUtils.isEmpty(resourceUri) ? ROOT_URI : resourceUri); if (!TextUtils.isEmpty(query)) { mSearchCriteria.setQuery(query); } } @Override public void onViewCreated(View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); listView = (JasperRecyclerView) view.findViewById(android.R.id.list); swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refreshLayout); emptyText = (TextView) view.findViewById(android.R.id.empty); swipeRefreshLayout.setOnRefreshListener(this); swipeRefreshLayout.setColorSchemeResources( R.color.js_blue, R.color.js_dark_blue, R.color.js_blue, R.color.js_dark_blue); listView.addOnScrollListener(new ScrollListener()); setDataAdapter(); loadPage(); } @Override public void onResume() { super.onResume(); ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); if (actionBar != null) { if (TextUtils.isEmpty(resourceLabel)) { actionBar.setTitle(getString(R.string.h_repository_label)); } else { actionBar.setTitle(resourceLabel); } } List<Analytics.Dimension> viewDimension = new ArrayList<>(); viewDimension.add(new Analytics.Dimension(Analytics.Dimension.RESOURCE_VIEW_HIT_KEY, viewType.name())); analytics.sendScreenView(Analytics.ScreenName.REPOSITORY.getValue(), viewDimension); } @Override public void onPause() { swipeRefreshLayout.clearAnimation(); super.onPause(); } @Override public void onDestroy() { super.onDestroy(); listView.setVisibility(View.GONE); } public void setQuery(String query) { this.query = query; } //--------------------------------------------------------------------- // Implements SwipeRefreshLayout.OnRefreshListener //--------------------------------------------------------------------- @Override public void onRefresh() { ImageLoader.getInstance().clearDiskCache(); ImageLoader.getInstance().clearMemoryCache(); loadPage(); analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.REFRESHED.getValue(), Analytics.EventLabel.REPOSITORY.getValue()); } //--------------------------------------------------------------------- // Helper methods //--------------------------------------------------------------------- private void clearData() { mResourceLookupHashMap.clear(); mAdapter.clear(); } private void addData(List<ResourceLookup> data) { Collections.sort(data, new OrderingByType()); mResourceLookupHashMap.putAll(jasperResourceConverter.convertToDataMap(data)); mAdapter.addAll(jasperResourceConverter.convertToJasperResource(data)); mAdapter.notifyDataSetChanged(); } private void onViewSingleClick(ResourceLookup resource) { resourceOpener.openResource(this, prefTag, resource); } private void setDataAdapter() { mAdapter = new JasperResourceAdapter(getActivity()); mAdapter.setOnItemInteractionListener(new JasperResourceAdapter.OnResourceInteractionListener() { @Override public void onResourceItemClicked(JasperResource jasperResource) { onViewSingleClick(mResourceLookupHashMap.get(jasperResource.getId())); } @Override public void onSecondaryActionClicked(JasperResource jasperResource) { ResourceInfoActivity_.intent(getActivity()) .jasperResource(jasperResource) .start(); } }); listView.setAdapter(mAdapter); listView.changeViewType(viewType); } private void loadRootFolders() { setRefreshState(true); mGetRootFoldersCase.execute(new GetRootFolderDataRequestListener()); } private void loadNextPage() { if (!mLoading && mHasNextPage) { int currentOffset = mSearchCriteria.getOffset(); mSearchCriteria.setOffset(currentOffset + mLimit); loadResources(); analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.LOADED_NEXT.getValue(), Analytics.EventLabel.REPOSITORY.getValue()); } } private void loadResources() { setRefreshState(true); mSearchResourcesCase.execute(mSearchCriteria, new GetResourceLookupsListener()); } private void showEmptyText(int resId) { boolean noItems = (mAdapter.getItemCount() > 0); emptyText.setVisibility(noItems ? View.GONE : View.VISIBLE); if (resId != 0) emptyText.setText(resId); } private void setRefreshState(boolean refreshing) { mLoading = refreshing; if (!refreshing) { swipeRefreshLayout.setRefreshing(false); mAdapter.hideLoading(); } else { mAdapter.showLoading(); } } private void loadPage() { boolean isRoot = TextUtils.isEmpty(resourceUri); boolean isProJrs = mServer.isProEdition(); clearData(); mSearchCriteria.setOffset(0); mSearchResourcesCase.unsubscribe(); if (isRoot && isProJrs) { loadRootFolders(); } else { loadResources(); } } //--------------------------------------------------------------------- // Inner classes //--------------------------------------------------------------------- private static class OrderingByType implements Comparator<ResourceLookup> { @Override public int compare(ResourceLookup res1, ResourceLookup res2) { int lhs = res1.getResourceType() == ResourceLookup.ResourceType.folder ? 0 : 1; int rhs = res2.getResourceType() == ResourceLookup.ResourceType.folder ? 0 : 1; return compare(lhs, rhs); } private static int compare(int lhs, int rhs) { return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1); } } private class GetRootFolderDataRequestListener extends GenericSubscriber<List<FolderDataResponse>> { @Override public void onNext(List<FolderDataResponse> folderDataResponses) { List<ResourceLookup> datum = new ArrayList<>(); datum.addAll(folderDataResponses); addData(datum); showEmptyText(R.string.resources_not_found); } } private class GetResourceLookupsListener extends GenericSubscriber<SearchResult> { @Override public void onNext(SearchResult searchResult) { mHasNextPage = !searchResult.isReachedEnd(); addData(searchResult.getLookups()); setRefreshState(false); showEmptyText(R.string.resources_not_found); } } private abstract class GenericSubscriber<R> extends Subscriber<R> { @Override public void onStart() { showEmptyText(com.jaspersoft.android.jaspermobile.R.string.loading_msg); } @Override public void onCompleted() { setRefreshState(false); } @Override public void onError(Throwable e) { if (!isAdded()) return; Timber.e(e, "RepositoryFragment#GetRootFolderDataRequestListener failed"); RequestExceptionHandler.showAuthErrorIfExists(getActivity(), e); showEmptyText(com.jaspersoft.android.jaspermobile.R.string.failed_load_data); setRefreshState(false); } } //--------------------------------------------------------------------- // Implements AbsListView.OnScrollListener //--------------------------------------------------------------------- private class ScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); int visibleItemCount = recyclerView.getChildCount(); int totalItemCount = recyclerView.getLayoutManager().getItemCount(); int firstVisibleItem; if (layoutManager instanceof LinearLayoutManager) { firstVisibleItem = ((LinearLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition(); } else { firstVisibleItem = ((GridLayoutManager) layoutManager).findFirstCompletelyVisibleItemPosition(); } if (totalItemCount > 0 && firstVisibleItem + visibleItemCount >= totalItemCount - mTreshold) { loadNextPage(); } } } }