/*
* Copyright (c) 2015 Ha Duy Trung
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.hidroh.materialistic.widget;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import javax.inject.Inject;
import io.github.hidroh.materialistic.AlertDialogBuilder;
import io.github.hidroh.materialistic.AppUtils;
import io.github.hidroh.materialistic.CustomTabsDelegate;
import io.github.hidroh.materialistic.Injectable;
import io.github.hidroh.materialistic.ItemActivity;
import io.github.hidroh.materialistic.MultiPaneListener;
import io.github.hidroh.materialistic.Preferences;
import io.github.hidroh.materialistic.R;
import io.github.hidroh.materialistic.accounts.UserServices;
import io.github.hidroh.materialistic.data.FavoriteManager;
import io.github.hidroh.materialistic.data.ItemManager;
import io.github.hidroh.materialistic.data.WebItem;
/**
* Base {@link android.support.v7.widget.RecyclerView.Adapter} class for list items
* @param <VH> view holder type, should contain title, posted, source and comment views
* @param <T> item type, should provide title, posted, source
*/
public abstract class ListRecyclerViewAdapter
<VH extends ListRecyclerViewAdapter.ItemViewHolder, T extends WebItem>
extends RecyclerView.Adapter<VH> {
private static final String STATE_LAST_SELECTION_POSITION = "state:lastSelectedPosition";
private static final int VIEW_TYPE_CARD = 0;
private static final int VIEW_TYPE_FLAT = 1;
private CustomTabsDelegate mCustomTabsDelegate;
protected Context mContext;
private MultiPaneListener mMultiPaneListener;
protected RecyclerView mRecyclerView;
LayoutInflater mInflater;
@Inject PopupMenu mPopupMenu;
@Inject AlertDialogBuilder mAlertDialogBuilder;
@Inject UserServices mUserServices;
@Inject FavoriteManager mFavoriteManager;
private int mLastSelectedPosition = -1;
private int mCardElevation;
private boolean mCardViewEnabled = true;
private int mHotThreshold = Integer.MAX_VALUE;
private final Preferences.Observable mPreferenceObservable = new Preferences.Observable();
private boolean mMultiWindowEnabled;
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
mRecyclerView = recyclerView;
mContext = recyclerView.getContext();
mInflater = AppUtils.createLayoutInflater(mContext);
((Injectable) mContext).inject(this);
mMultiPaneListener = (MultiPaneListener) mContext;
mCardElevation = mContext.getResources()
.getDimensionPixelSize(R.dimen.cardview_default_elevation);
mMultiWindowEnabled = Preferences.multiWindowEnabled(mContext);
mPreferenceObservable.subscribe(mContext, (key, contextChanged) ->
mMultiWindowEnabled = Preferences.multiWindowEnabled(mContext),
R.string.pref_multi_window);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
mPreferenceObservable.unsubscribe(mContext);
mContext = null;
mMultiPaneListener = null;
mRecyclerView = null;
}
@Override
public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
VH holder = create(parent, viewType);
if (viewType == VIEW_TYPE_FLAT) {
holder.mCardView.flatten();
}
return holder;
}
@Override
public final void onBindViewHolder(final VH holder, int position) {
final T item = getItem(position);
holder.mCardView.setCardElevation(isSelected(item.getId()) ? mCardElevation * 2 :
(mCardViewEnabled ? mCardElevation : 0));
clearViewHolder(holder);
if (!isItemAvailable(item)) {
loadItem(holder.getAdapterPosition());
return;
}
// TODO naive launch priority for now
mCustomTabsDelegate.mayLaunchUrl(Uri.parse(item.getUrl()), null, null);
holder.mStoryView.setStory(item, mHotThreshold);
holder.mStoryView.setChecked(isSelected(item.getId()));
holder.itemView.setOnClickListener(v -> handleItemClick(item, holder));
holder.mStoryView.setOnCommentClickListener(v -> openItem(item));
bindItem(holder);
}
@Override
public final int getItemViewType(int position) {
return mCardViewEnabled ? VIEW_TYPE_CARD : VIEW_TYPE_FLAT;
}
@Override
public final long getItemId(int position) {
return getItem(position).getLongId();
}
public final boolean isCardViewEnabled() {
return mCardViewEnabled;
}
public final void setCardViewEnabled(boolean cardViewEnabled) {
this.mCardViewEnabled = cardViewEnabled;
notifyDataSetChanged();
}
public void setCustomTabsDelegate(CustomTabsDelegate customTabsDelegate) {
mCustomTabsDelegate = customTabsDelegate;
}
public void setHotThresHold(int hotThresHold) {
mHotThreshold = hotThresHold;
}
public Bundle saveState() {
Bundle savedState = new Bundle();
savedState.putInt(STATE_LAST_SELECTION_POSITION, mLastSelectedPosition);
return savedState;
}
public void restoreState(Bundle savedState) {
if (savedState == null) {
return;
}
mLastSelectedPosition = savedState.getInt(STATE_LAST_SELECTION_POSITION);
}
final boolean isAttached() {
return mContext != null;
}
protected abstract VH create(ViewGroup parent, int viewType);
protected void loadItem(int adapterPosition) {
// override to load item if needed
}
protected abstract void bindItem(VH holder);
protected abstract boolean isItemAvailable(T item);
private void clearViewHolder(VH holder) {
holder.mStoryView.reset();
holder.itemView.setOnClickListener(null);
holder.itemView.setOnLongClickListener(null);
}
/**
* Checks if item with given ID has been selected
* @param itemId item ID to check
* @return true if selected, false otherwise or if selection is disabled
*/
protected boolean isSelected(String itemId) {
return mMultiPaneListener.isMultiPane() &&
mMultiPaneListener.getSelectedItem() != null &&
itemId.equals(mMultiPaneListener.getSelectedItem().getId());
}
/**
* Gets item at position
* @param position item position
* @return item at given position or null
*/
protected abstract T getItem(int position);
/**
* Handles item click
* @param item clicked item
* @param holder clicked item view holder
*/
protected void handleItemClick(T item, VH holder) {
mMultiPaneListener.onItemSelected(item);
if (isSelected(item.getId())) {
notifyItemChanged(holder.getAdapterPosition());
if (mLastSelectedPosition >= 0) {
notifyItemChanged(mLastSelectedPosition);
}
mLastSelectedPosition = holder.getAdapterPosition();
}
}
/**
* Gets cache mode for {@link ItemManager}
*
* @return cache mode
*/
@ItemManager.CacheMode
protected abstract int getItemCacheMode();
private void openItem(T item) {
Intent intent = new Intent(mContext, ItemActivity.class)
.putExtra(ItemActivity.EXTRA_CACHE_MODE, getItemCacheMode())
.putExtra(ItemActivity.EXTRA_ITEM, item)
.putExtra(ItemActivity.EXTRA_OPEN_COMMENTS, true);
mContext.startActivity(mMultiWindowEnabled ?
AppUtils.multiWindowIntent((Activity) mContext, intent) : intent);
}
/**
* Base {@link android.support.v7.widget.RecyclerView.ViewHolder} class for list item view
*/
static class ItemViewHolder extends RecyclerView.ViewHolder {
final StoryView mStoryView;
@SuppressWarnings("WeakerAccess")
final FlatCardView mCardView;
ItemViewHolder(View itemView) {
super(itemView);
mCardView = (FlatCardView) itemView;
mStoryView = (StoryView) itemView.findViewById(R.id.story_view);
}
}
}