/*
* 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.library.fragment;
import android.content.Intent;
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 android.widget.Toast;
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.activities.library.LibrarySearchableActivity_;
import com.jaspersoft.android.jaspermobile.dialog.ProgressDialogFragment;
import com.jaspersoft.android.jaspermobile.domain.SearchResult;
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.VoiceRecognitionHelper;
import com.jaspersoft.android.jaspermobile.util.filtering.LibraryResourceFilter;
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.util.sorting.SortOrder;
import com.jaspersoft.android.jaspermobile.widget.JasperRecyclerView;
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.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 LibraryFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {
public static final String TAG = LibraryFragment.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;
@InstanceState
@FragmentArg
protected String resourceLabel;
@InstanceState
@FragmentArg
protected SortOrder sortOrder;
@InstanceState
@FragmentArg
protected String query;
@FragmentArg
protected ViewType viewType;
@Inject
@Named("LIMIT")
protected int mLimit;
@Inject
@Named("THRESHOLD")
protected int mTreshold;
@Inject
protected Analytics analytics;
@Inject
protected JasperResourceConverter jasperResourceConverter;
@Inject
protected SearchResourcesCase mSearchResourcesCase;
@Inject
protected FavoritesHelper favoritesHelper;
@InstanceState
protected boolean mLoading;
@InstanceState
protected boolean mHasNextPage = true;
@Bean
protected LibraryResourceFilter libraryResourceFilter;
@Bean
protected DefaultPrefHelper prefHelper;
@Bean
protected ResourceOpener resourceOpener;
private JasperResourceAdapter mAdapter;
private HashMap<String, ResourceLookup> mResourceLookupHashMap;
private 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(true);
mSearchCriteria.setTypes(libraryResourceFilter.getCurrent().getValues());
mSearchCriteria.setFolderUri(ROOT_URI);
if (!TextUtils.isEmpty(query)) {
mSearchCriteria.setQuery(query);
}
if (sortOrder != null) {
mSearchCriteria.setSortBy(sortOrder.getValue());
}
}
@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();
loadFirstPage();
}
@Override
public void onResume() {
super.onResume();
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
if (TextUtils.isEmpty(resourceLabel)) {
actionBar.setTitle(getString(R.string.h_library_label));
} else {
actionBar.setTitle(resourceLabel);
}
}
List<Analytics.Dimension> viewDimension = new ArrayList<>();
viewDimension.add(new Analytics.Dimension(Analytics.Dimension.FILTER_TYPE_HIT_KEY, libraryResourceFilter.getCurrent().getName()));
viewDimension.add(new Analytics.Dimension(Analytics.Dimension.RESOURCE_VIEW_HIT_KEY, viewType.name()));
analytics.sendScreenView(Analytics.ScreenName.LIBRARY.getValue(), viewDimension);
}
@Override
public void onPause() {
swipeRefreshLayout.clearAnimation();
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
listView.setVisibility(View.GONE);
}
@Override
public void onDestroyView() {
mSearchResourcesCase.unsubscribe();
super.onDestroyView();
}
public void setQuery(String query) {
this.query = query;
}
//---------------------------------------------------------------------
// Implements SwipeRefreshLayout.OnRefreshListener
//---------------------------------------------------------------------
@Override
public void onRefresh() {
ImageLoader.getInstance().clearDiskCache();
ImageLoader.getInstance().clearMemoryCache();
loadFirstPage();
analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.REFRESHED.getValue(), Analytics.EventLabel.LIBRARY.getValue());
}
//---------------------------------------------------------------------
// Implements ResourcesLoader
//---------------------------------------------------------------------
public void loadResourcesByTypes() {
analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.FILTERED.getValue(), libraryResourceFilter.getCurrent().getName());
mSearchCriteria.setTypes(libraryResourceFilter.getCurrent().getValues());
loadFirstPage();
}
public void loadResourcesBySortOrder(SortOrder order) {
analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.SORTED.getValue(), sortOrder.name());
sortOrder = order;
mSearchCriteria.setSortBy(order.getValue());
loadFirstPage();
}
public void handleVoiceCommand(ArrayList<String> matches) {
VoiceRecognitionHelper.VoiceCommand voiceCommand = VoiceRecognitionHelper.parseCommand(matches);
String voiceCommandName;
switch (voiceCommand.getCommand()) {
case VoiceRecognitionHelper.VoiceCommand.FIND:
Intent searchIntent = LibrarySearchableActivity_
.intent(getActivity())
.query(voiceCommand.getArgument())
.get();
getActivity().startActivity(searchIntent);
voiceCommandName = Analytics.EventLabel.FIND.getValue();
break;
case VoiceRecognitionHelper.VoiceCommand.RUN:
requestResourceLookup(voiceCommand.getArgument());
voiceCommandName = Analytics.EventLabel.RUN.getValue();
break;
default:
Toast.makeText(getActivity(), R.string.voice_command_undefined, Toast.LENGTH_SHORT).show();
voiceCommandName = Analytics.EventLabel.UNDEFINED.getValue();
break;
}
analytics.sendEvent(Analytics.EventCategory.CATALOG.getValue(), Analytics.EventAction.SAID_COMMANDS.getValue(), voiceCommandName);
}
//---------------------------------------------------------------------
// Helper methods
//---------------------------------------------------------------------
private void clearData() {
mResourceLookupHashMap.clear();
mAdapter.clear();
}
private void addData(List<ResourceLookup> data) {
mResourceLookupHashMap.putAll(jasperResourceConverter.convertToDataMap(data));
mAdapter.addAll(jasperResourceConverter.convertToJasperResource(data));
mAdapter.notifyDataSetChanged();
}
private void onViewSingleClick(ResourceLookup resource) {
resourceOpener.openResource(this, 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 loadFirstPage() {
clearData();
mSearchCriteria.setOffset(0);
mSearchResourcesCase.unsubscribe();
loadResources();
}
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.LIBRARY.getValue());
}
}
private void loadResources() {
mSearchResourcesCase.unsubscribe();
setRefreshState(true);
showEmptyTextIfNoItems(R.string.loading_msg);
mSearchResourcesCase.execute(mSearchCriteria, new GetResourceLookupsListener());
}
private void requestResourceLookup(String label) {
List<String> resTypes = Collections.singletonList(
ResourceLookup.ResourceType.reportUnit.name()
);
ResourceLookupSearchCriteria criteria = new ResourceLookupSearchCriteria();
criteria.setFolderUri(ROOT_URI);
criteria.setTypes(resTypes);
criteria.setQuery(label);
criteria.setRecursive(true);
criteria.setOffset(0);
criteria.setLimit(1);
mSearchResourcesCase.execute(criteria, new GetResourceMetadataListener(label));
}
private void showEmptyTextIfNoItems(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 hideLoading() {
ProgressDialogFragment.dismiss(getActivity().getSupportFragmentManager());
}
private void showLoading() {
ProgressDialogFragment.builder(getActivity().getSupportFragmentManager())
.setLoadingMessage(R.string.loading_msg)
.show();
}
//---------------------------------------------------------------------
// Inner classes
//---------------------------------------------------------------------
private class GetResourceLookupsListener extends Subscriber<SearchResult> {
@Override
public void onCompleted() {
setRefreshState(false);
}
@Override
public void onError(Throwable e) {
if (!isAdded()) return;
Timber.e(e, "LibraryFragment#GetResourceLookupsListener failed");
RequestExceptionHandler.showAuthErrorIfExists(getContext(), e);
showEmptyTextIfNoItems(R.string.failed_load_data);
setRefreshState(false);
}
@Override
public void onNext(SearchResult result) {
mHasNextPage = !result.isReachedEnd();
addData(result.getLookups());
showEmptyTextIfNoItems(R.string.resources_not_found);
}
}
private class GetResourceMetadataListener extends Subscriber<SearchResult> {
private String mResourceQuery;
public GetResourceMetadataListener(String mResourceQuery) {
this.mResourceQuery = mResourceQuery;
}
@Override
public void onStart() {
showLoading();
}
@Override
public void onCompleted() {
hideLoading();
}
@Override
public void onError(Throwable e) {
Timber.e(e, "LibraryFragment#GetResourceMetadataListener failed");
RequestExceptionHandler.showAuthErrorIfExists(getContext(), e);
hideLoading();
}
@Override
public void onNext(SearchResult searchResult) {
List<ResourceLookup> resourceLookupsList = searchResult.getLookups();
if (resourceLookupsList.isEmpty()) {
Toast.makeText(getActivity(), "Can not find " + "\"" + mResourceQuery + "\"", Toast.LENGTH_SHORT).show();
} else {
resourceOpener.openResource(LibraryFragment.this, resourceLookupsList.get(0));
}
}
}
//---------------------------------------------------------------------
// 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();
}
}
}
}