/*
* Copyright 2015 Google Inc.
*
* 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.plaidapp.ui;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.plaidapp.R;
import io.plaidapp.data.Source;
import io.plaidapp.data.prefs.DribbblePrefs;
import io.plaidapp.data.prefs.SourceManager;
import io.plaidapp.ui.recyclerview.ItemTouchHelperAdapter;
import io.plaidapp.util.ColorUtils;
import io.plaidapp.util.ViewUtils;
/**
* Adapter for showing the list of data sources used as filters for the home grid.
*/
public class FilterAdapter extends RecyclerView.Adapter<FilterAdapter.FilterViewHolder>
implements ItemTouchHelperAdapter, DribbblePrefs.DribbbleLoginStatusListener {
public interface FilterAuthoriser {
void requestDribbbleAuthorisation(View sharedElement, Source forSource);
}
private static final int FILTER_ICON_ENABLED_ALPHA = 179; // 70%
private static final int FILTER_ICON_DISABLED_ALPHA = 51; // 20%
private final List<Source> filters;
private final FilterAuthoriser authoriser;
private final Context context;
private @Nullable List<FiltersChangedListener> listeners;
public FilterAdapter(@NonNull Context context,
@NonNull List<Source> filters,
@NonNull FilterAuthoriser authoriser) {
this.context = context.getApplicationContext();
this.filters = filters;
this.authoriser = authoriser;
setHasStableIds(true);
}
public List<Source> getFilters() {
return filters;
}
/**
* Adds a new data source to the list of filters. If the source already exists then it is simply
* activated.
*
* @param toAdd the source to add
* @return whether the filter was added (i.e. if it did not already exist)
*/
public boolean addFilter(Source toAdd) {
// first check if it already exists
final int count = filters.size();
for (int i = 0; i < count; i++) {
Source existing = filters.get(i);
if (existing.getClass() == toAdd.getClass()
&& existing.key.equalsIgnoreCase(toAdd.key)) {
// already exists, just ensure it's active
if (!existing.active) {
existing.active = true;
dispatchFiltersChanged(existing);
notifyItemChanged(i);
SourceManager.updateSource(existing, context);
}
return false;
}
}
// didn't already exist, so add it
filters.add(toAdd);
Collections.sort(filters, new Source.SourceComparator());
dispatchFiltersChanged(toAdd);
notifyDataSetChanged();
SourceManager.addSource(toAdd, context);
return true;
}
public void removeFilter(Source removing) {
int position = filters.indexOf(removing);
filters.remove(position);
notifyItemRemoved(position);
dispatchFilterRemoved(removing);
SourceManager.removeSource(removing, context);
}
public int getFilterPosition(Source filter) {
return filters.indexOf(filter);
}
public void enableFilterByKey(@NonNull String key, @NonNull Context context) {
final int count = filters.size();
for (int i = 0; i < count; i++) {
Source filter = filters.get(i);
if (filter.key.equals(key)) {
if (!filter.active) {
filter.active = true;
notifyItemChanged(i);
dispatchFiltersChanged(filter);
SourceManager.updateSource(filter, context);
return;
}
}
}
}
@Override
public FilterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
return new FilterViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.filter_item, viewGroup, false));
}
@Override
public void onBindViewHolder(final FilterViewHolder vh, final int position) {
final Source filter = filters.get(position);
vh.isSwipeable = filter.isSwipeDismissable();
vh.filterName.setText(filter.name);
vh.filterName.setEnabled(filter.active);
if (filter.iconRes > 0) {
vh.filterIcon.setImageDrawable(vh.itemView.getContext().getDrawable(filter.iconRes));
}
vh.filterIcon.setImageAlpha(filter.active ? FILTER_ICON_ENABLED_ALPHA :
FILTER_ICON_DISABLED_ALPHA);
vh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isAuthorisedDribbbleSource(filter) &&
!DribbblePrefs.get(vh.itemView.getContext()).isLoggedIn()) {
authoriser.requestDribbbleAuthorisation(vh.filterIcon, filter);
} else {
vh.itemView.setHasTransientState(true);
ObjectAnimator fade = ObjectAnimator.ofInt(vh.filterIcon, ViewUtils.IMAGE_ALPHA,
filter.active ? FILTER_ICON_DISABLED_ALPHA : FILTER_ICON_ENABLED_ALPHA);
fade.setDuration(300);
fade.setInterpolator(AnimationUtils.loadInterpolator(vh.itemView.getContext()
, android.R.interpolator.fast_out_slow_in));
fade.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
vh.itemView.setHasTransientState(false);
}
});
fade.start();
filter.active = !filter.active;
vh.filterName.setEnabled(filter.active);
notifyItemChanged(position);
SourceManager.updateSource(filter, vh.itemView.getContext());
dispatchFiltersChanged(filter);
}
}
});
}
@Override
public int getItemCount() {
return filters.size();
}
@Override
public long getItemId(int position) {
return filters.get(position).key.hashCode();
}
private boolean isAuthorisedDribbbleSource(Source source) {
return source.key.equals(SourceManager.SOURCE_DRIBBBLE_FOLLOWING)
|| source.key.equals(SourceManager.SOURCE_DRIBBBLE_USER_LIKES)
|| source.key.equals(SourceManager.SOURCE_DRIBBBLE_USER_SHOTS);
}
@Override
public void onItemDismiss(int position) {
Source removing = filters.get(position);
if (removing.isSwipeDismissable()) {
removeFilter(removing);
}
}
public int getEnabledSourcesCount() {
int count = 0;
for (Source source : filters) {
if (source.active) {
count++;
}
}
return count;
}
public void addFilterChangedListener(FiltersChangedListener listener) {
if (listeners == null) {
listeners = new ArrayList<>();
}
listeners.add(listener);
}
public void removeFilterChangedListener(FiltersChangedListener listener) {
if (listeners != null) {
listeners.remove(listener);
}
}
private void dispatchFiltersChanged(Source filter) {
if (listeners != null) {
for (FiltersChangedListener listener : listeners) {
listener.onFiltersChanged(filter);
}
}
}
private void dispatchFilterRemoved(Source filter) {
if (listeners != null) {
for (FiltersChangedListener listener : listeners) {
listener.onFilterRemoved(filter);
}
}
}
public interface FiltersChangedListener {
void onFiltersChanged(Source changedFilter);
void onFilterRemoved(Source removed);
}
public static class FilterViewHolder extends RecyclerView.ViewHolder {
public TextView filterName;
public ImageView filterIcon;
public boolean isSwipeable;
public FilterViewHolder(View itemView) {
super(itemView);
filterName = (TextView) itemView.findViewById(R.id.filter_name);
filterIcon = (ImageView) itemView.findViewById(R.id.filter_icon);
}
public void highlightFilter() {
itemView.setHasTransientState(true);
int highlightColor = ContextCompat.getColor(itemView.getContext(), R.color.accent);
int fadeFromTo = ColorUtils.modifyAlpha(highlightColor, 0);
ObjectAnimator background = ObjectAnimator.ofArgb(
itemView,
ViewUtils.BACKGROUND_COLOR,
fadeFromTo,
highlightColor,
fadeFromTo);
background.setDuration(1000L);
background.setInterpolator(AnimationUtils.loadInterpolator(itemView.getContext(),
android.R.interpolator.linear));
background.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
itemView.setBackground(null);
itemView.setHasTransientState(false);
}
});
background.start();
}
}
@Override
public void onDribbbleLogin() {
// no-op
}
@Override
public void onDribbbleLogout() {
boolean changed = false;
for (Source filter : filters) {
if (filter.active && isAuthorisedDribbbleSource(filter)) {
filter.active = false;
SourceManager.updateSource(filter, context);
dispatchFiltersChanged(filter);
changed = true;
}
}
if (changed) {
notifyDataSetChanged();
}
}
}