package com.wangdaye.mysplash.common.ui.dialog;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.RequiresApi;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.github.rahatarmanahmed.cpv.CircularProgressView;
import com.wangdaye.mysplash.Mysplash;
import com.wangdaye.mysplash.R;
import com.wangdaye.mysplash.common.data.entity.unsplash.ChangeCollectionPhotoResult;
import com.wangdaye.mysplash.common.data.entity.unsplash.Collection;
import com.wangdaye.mysplash.common.data.entity.unsplash.Me;
import com.wangdaye.mysplash.common.data.entity.unsplash.Photo;
import com.wangdaye.mysplash.common.data.entity.unsplash.User;
import com.wangdaye.mysplash.common.data.service.CollectionService;
import com.wangdaye.mysplash.common._basic.MysplashDialogFragment;
import com.wangdaye.mysplash.common.ui.widget.swipeRefreshView.BothWaySwipeRefreshLayout;
import com.wangdaye.mysplash.common.utils.DisplayUtils;
import com.wangdaye.mysplash.common.utils.helper.NotificationHelper;
import com.wangdaye.mysplash.common.utils.manager.AuthManager;
import com.wangdaye.mysplash.common.ui.adapter.CollectionMiniAdapter;
import com.wangdaye.mysplash.common.utils.AnimUtils;
import com.wangdaye.mysplash.common.utils.manager.ThemeManager;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import retrofit2.Call;
import retrofit2.Response;
/**
* Select collection dialog.
*
* This dialog is used to deal some operation about collections, like add or remove photos or create
* new collections.
*
* */
public class SelectCollectionDialog extends MysplashDialogFragment
implements AuthManager.OnAuthDataChangedListener,
CollectionMiniAdapter.OnCollectionResponseListener, CollectionService.OnRequestACollectionListener {
@BindView(R.id.dialog_select_collection_container)
CoordinatorLayout container;
@BindView(R.id.dialog_select_collection_progressView)
CircularProgressView progressView;
@BindView(R.id.dialog_select_collection_selectorContainer)
LinearLayout selectorContainer;
@BindView(R.id.dialog_select_collection_titleBar)
RelativeLayout selectorTitleBar;
@BindView(R.id.dialog_select_collection_selectorRefreshView)
BothWaySwipeRefreshLayout refreshLayout;
@BindView(R.id.dialog_select_collection_selectorRecyclerView)
RecyclerView recyclerView;
@BindView(R.id.dialog_select_collection_creatorContainer)
LinearLayout creatorContainer;
@BindView(R.id.dialog_select_collection_creatorName)
EditText nameTxt;
@BindView(R.id.dialog_select_collection_creatorDescription)
EditText descriptionTxt;
@BindView(R.id.dialog_select_collection_creatorCheckBox)
CheckBox checkBox;
private OnCollectionsChangedListener listener;
private Me me;
private Photo photo;
private int page; // HTTP request param.
private CollectionMiniAdapter adapter;
private CollectionService service;
private OnRequestCollectionsListener serviceListener;
@StateRule
private int state;
private static final int SHOW_COLLECTIONS_STATE = 0;
private static final int INPUT_COLLECTION_STATE = 1;
private static final int CREATE_COLLECTION_STATE = 2;
@IntDef({SHOW_COLLECTIONS_STATE, INPUT_COLLECTION_STATE, CREATE_COLLECTION_STATE})
private @interface StateRule {}
private boolean usable; // if set false, it means the dialog has been destroyed.
@SuppressLint("InflateParams")
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
View view = LayoutInflater.from(getActivity())
.inflate(R.layout.dialog_select_collection, null, false);
ButterKnife.bind(this, view);
initData();
initWidget(view);
AuthManager.getInstance().addOnWriteDataListener(this);
if (!AuthManager.getInstance().getCollectionsManager().isLoadFinish()) {
AuthManager.getInstance().getCollectionsManager().clearCollections();
initRefresh();
}
return new AlertDialog.Builder(getActivity())
.setView(view)
.create();
}
@Override
public void onDestroy() {
super.onDestroy();
usable = false;
AuthManager.getInstance().removeOnWriteDataListener(this);
if (serviceListener != null) {
serviceListener.cancel();
}
}
@Override
public CoordinatorLayout getSnackbarContainer() {
return container;
}
// init.
private void initData() {
this.me = AuthManager.getInstance().getMe();
this.service = CollectionService.getService();
this.state = SHOW_COLLECTIONS_STATE;
this.page = 1;
this.adapter = new CollectionMiniAdapter(getActivity(), photo);
adapter.setOnCollectionResponseListener(this);
this.usable = true;
}
private void initWidget(View v) {
setCancelable(true);
progressView.setVisibility(View.GONE);
selectorContainer.setVisibility(View.VISIBLE);
ImageButton refreshBtn = ButterKnife.findById(v, R.id.dialog_select_collection_selectorRefreshBtn);
ThemeManager.setImageResource(refreshBtn, R.drawable.ic_refresh_light, R.drawable.ic_refresh_dark);
refreshLayout.setColorSchemeColors(ThemeManager.getContentColor(getActivity()));
refreshLayout.setProgressBackgroundColorSchemeColor(ThemeManager.getRootColor(getActivity()));
refreshLayout.setPermitRefresh(false);
refreshLayout.setPermitLoad(false);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
recyclerView.addOnScrollListener(new ElevationScrollListener());
}
recyclerView.addOnScrollListener(new LoadScrollListener());
creatorContainer.setVisibility(View.GONE);
DisplayUtils.setTypeface(getActivity(), nameTxt);
DisplayUtils.setTypeface(getActivity(), descriptionTxt);
DisplayUtils.setTypeface(getActivity(), checkBox);
}
// control.
// HTTP request.
private void initRefresh() {
if (AuthManager.getInstance().getState() == AuthManager.FREEDOM_STATE) {
if (AuthManager.getInstance().getMe() == null) {
requestProfile();
} else {
int listSize = AuthManager.getInstance()
.getCollectionsManager()
.getCollectionList()
.size();
if (listSize > 0) {
AuthManager.getInstance().getCollectionsManager().clearCollections();
adapter.notifyItemRangeRemoved(1, listSize);
adapter.notifyItemChanged(1);
}
page = 1;
requestCollections();
}
}
}
private void requestProfile() {
AuthManager.getInstance().requestPersonalProfile();
}
private void requestCollections() {
if (serviceListener != null) {
serviceListener.cancel();
}
service.cancel();
serviceListener = new OnRequestCollectionsListener();
service.requestUserCollections(me.username, page, Mysplash.DEFAULT_PER_PAGE, serviceListener);
}
private void createCollection() {
String title = nameTxt.getText().toString();
if (TextUtils.isEmpty(title)) {
NotificationHelper.showSnackbar(
getString(R.string.feedback_name_is_required),
Snackbar.LENGTH_SHORT);
} else {
String description = TextUtils.isEmpty(descriptionTxt.getText().toString()) ?
null : descriptionTxt.getText().toString();
boolean privateX = checkBox.isChecked();
service.createCollection(
title,
description,
privateX,
this);
setState(CREATE_COLLECTION_STATE);
}
}
// state.
private void setState(@StateRule int newState) {
switch (newState) {
case SHOW_COLLECTIONS_STATE:
setCancelable(true);
if (state == CREATE_COLLECTION_STATE) {
AnimUtils.animShow(selectorContainer);
AnimUtils.animHide(progressView);
} else if (state == INPUT_COLLECTION_STATE) {
AnimUtils.animShow(selectorContainer);
AnimUtils.animHide(creatorContainer);
}
break;
case INPUT_COLLECTION_STATE:
setCancelable(true);
if (state == SHOW_COLLECTIONS_STATE) {
AnimUtils.animShow(creatorContainer);
AnimUtils.animHide(selectorContainer);
} else if (state == CREATE_COLLECTION_STATE) {
AnimUtils.animShow(creatorContainer);
AnimUtils.animHide(progressView);
}
break;
case CREATE_COLLECTION_STATE:
setCancelable(false);
if (state == INPUT_COLLECTION_STATE) {
AnimUtils.animShow(progressView);
AnimUtils.animHide(creatorContainer);
}
break;
}
state = newState;
}
// keyboard.
private void hideKeyboard() {
InputMethodManager manager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
manager.hideSoftInputFromWindow(nameTxt.getWindowToken(), 0);
manager.hideSoftInputFromWindow(descriptionTxt.getWindowToken(), 0);
}
// feedback.
/**
* Update item view when a HTTP request completed.
* For example, add a photo to a collection successful.
*
* @param collectionId Collection id.
* @param add if set true, it means add photo to a collection. Otherwise, it means
* remove photo from a collection.
* @param succeed if set true, it means HTTP request successful, otherwise failed.
* */
private void notifySelectCollectionResult(int collectionId, boolean add, boolean succeed) {
for (int i = 0;
i < AuthManager.getInstance().getCollectionsManager().getCollectionList().size();
i ++) {
if (AuthManager.getInstance().getCollectionsManager().getCollectionList().get(i).id == collectionId) {
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int position = i + 1;
int firstPosition = layoutManager.findFirstVisibleItemPosition();
int lastPosition = layoutManager.findLastVisibleItemPosition();
if (firstPosition <= position && position <= lastPosition) {
CollectionMiniAdapter.ViewHolder holder
= (CollectionMiniAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position);
holder.reloadCoverImage(
AuthManager.getInstance().getCollectionsManager().getCollectionList().get(i));
if (succeed) {
if (add) {
holder.setResultState(R.drawable.ic_item_state_succeed);
} else {
holder.setResultState(android.R.color.transparent);
}
} else {
if (add) {
holder.setResultState(android.R.color.transparent);
NotificationHelper.showSnackbar(
getString(R.string.feedback_add_photo_failed),
Snackbar.LENGTH_SHORT);
} else {
holder.setResultState(R.drawable.ic_item_state_succeed);
NotificationHelper.showSnackbar(
getString(R.string.feedback_delete_photo_failed),
Snackbar.LENGTH_SHORT);
}
}
return;
}
}
}
}
private void notifyCreateFailed() {
NotificationHelper.showSnackbar(
getString(R.string.feedback_create_collection_failed),
Snackbar.LENGTH_SHORT);
}
// interface.
public interface OnCollectionsChangedListener {
void onAddCollection(Collection c);
void onUpdateCollection(Collection c, User u, Photo p);
}
private void setOnCollectionsChangedListener(OnCollectionsChangedListener l) {
listener = l;
}
public void setPhotoAndListener(Photo p, OnCollectionsChangedListener l) {
photo = p;
setOnCollectionsChangedListener(l);
}
// on click listener.
@OnClick(R.id.dialog_select_collection_selectorRefreshBtn) void refresh() {
initRefresh();
refreshLayout.setLoading(true);
}
@OnClick(R.id.dialog_select_collection_creatorCreateBtn) void create() {
hideKeyboard();
createCollection();
}
@OnClick(R.id.dialog_select_collection_creatorCancelBtn) void cancel() {
hideKeyboard();
setState(SHOW_COLLECTIONS_STATE);
}
// on scroll listener.
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private class ElevationScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy){
selectorTitleBar.setElevation(
Math.min(5, selectorTitleBar.getElevation() + dy));
}
}
private class LoadScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy){
if (!ViewCompat.canScrollVertically(recyclerView, 1)
&& !AuthManager.getInstance().getCollectionsManager().isLoadFinish()) {
refreshLayout.setLoading(true);
}
}
}
// on auth data changed listener.
@Override
public void onWriteAccessToken() {
// do nothing.
}
@Override
public void onWriteUserInfo() {
if (me == null) {
me = AuthManager.getInstance().getMe();
requestCollections();
}
}
@Override
public void onWriteAvatarPath() {
// do nothing.
}
@Override
public void onLogout() {
// do nothing.
}
// on collection response listener (recycler view adapter item click).
@Override
public void onCreateCollection() {
setState(INPUT_COLLECTION_STATE);
}
@Override
public void onClickCollectionItem(int collectionId, int adapterPosition) {
CollectionMiniAdapter.ViewHolder holder
= (CollectionMiniAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(adapterPosition);
holder.setProgressState();
for (int i = 0; i < photo.current_user_collections.size(); i ++) {
if (collectionId == photo.current_user_collections.get(i).id) {
service.deletePhotoFromCollection(
collectionId,
photo.id,
new OnChangeCollectionPhotoListener(collectionId, false));
return;
}
}
service.addPhotoToCollection(
collectionId,
photo.id,
new OnChangeCollectionPhotoListener(collectionId, true));
}
// on request collections listener (request collections list.).
private class OnRequestCollectionsListener
implements CollectionService.OnRequestCollectionsListener {
private boolean canceled;
OnRequestCollectionsListener() {
canceled = false;
}
public void cancel() {
this.canceled = true;
}
// interface.
@Override
public void onRequestCollectionsSuccess(Call<List<Collection>> call, Response<List<Collection>> response) {
if (canceled) {
return;
}
if (response.isSuccessful() && response.body() != null) {
refreshLayout.setLoading(false);
if (response.body().size() > 0) {
int startPosition = AuthManager.getInstance()
.getCollectionsManager()
.getCollectionList()
.size() + 1;
AuthManager.getInstance()
.getCollectionsManager()
.addCollections(response.body());
adapter.notifyItemRangeInserted(startPosition, response.body().size());
}
if (response.body().size() < Mysplash.DEFAULT_PER_PAGE) {
AuthManager.getInstance().getCollectionsManager().setLoadFinish(true);
} else {
page ++;
requestCollections();
}
} else {
requestCollections();
}
}
@Override
public void onRequestCollectionsFailed(Call<List<Collection>> call, Throwable t) {
if (canceled) {
return;
}
requestCollections();
}
}
// on request a collection listener (create collection).
@Override
public void onRequestACollectionSuccess(Call<Collection> call, Response<Collection> response) {
if (response.isSuccessful() && response.body() != null) {
AuthManager.getInstance().getCollectionsManager().addCollectionToFirst(response.body());
adapter.notifyItemInserted(1);
setState(SHOW_COLLECTIONS_STATE);
nameTxt.setText("");
descriptionTxt.setText("");
checkBox.setSelected(false);
if (listener != null) {
listener.onAddCollection(response.body());
}
} else {
setState(INPUT_COLLECTION_STATE);
notifyCreateFailed();
}
}
@Override
public void onRequestACollectionFailed(Call<Collection> call, Throwable t) {
setState(INPUT_COLLECTION_STATE);
notifyCreateFailed();
}
// on change collection photo listener (add photo or delete photo).
private class OnChangeCollectionPhotoListener
implements CollectionService.OnChangeCollectionPhotoListener {
private int collectionId;
private boolean add;
OnChangeCollectionPhotoListener(int collectionId, boolean add) {
this.collectionId = collectionId;
this.add = add;
}
@Override
public void onChangePhotoSuccess(Call<ChangeCollectionPhotoResult> call,
Response<ChangeCollectionPhotoResult> response) {
if (usable) {
if (response.isSuccessful() && response.body() != null) {
if (listener != null) {
listener.onUpdateCollection(
response.body().collection,
response.body().user,
response.body().photo);
}
// update collection.
AuthManager.getInstance().getCollectionsManager().updateCollection(response.body().collection);
// update user.
AuthManager.getInstance().updateUser(response.body().user);
// update photo.
photo = response.body().photo;
adapter.updatePhoto(photo);
// update view.
notifySelectCollectionResult(response.body().collection.id, add, true);
} else {
notifySelectCollectionResult(response.body().collection.id, add, false);
}
}
}
@Override
public void onChangePhotoFailed(Call<ChangeCollectionPhotoResult> call, Throwable t) {
if (usable) {
notifySelectCollectionResult(collectionId, add, false);
}
}
}
}