/* * Copyright (c) 2016 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com> * All Rights Reserved. */ package me.zhanghai.android.douya.broadcast.ui; import android.app.Activity; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.support.v4.app.SharedElementCallback; import android.support.v4.view.ViewCompat; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ProgressBar; import com.android.volley.VolleyError; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import me.zhanghai.android.customtabshelper.CustomTabsHelperFragment; import me.zhanghai.android.douya.R; import me.zhanghai.android.douya.broadcast.content.BroadcastAndCommentListResource; import me.zhanghai.android.douya.broadcast.content.BroadcastCommentCountFixer; import me.zhanghai.android.douya.broadcast.content.DeleteBroadcastCommentManager; import me.zhanghai.android.douya.broadcast.content.DeleteBroadcastManager; import me.zhanghai.android.douya.broadcast.content.LikeBroadcastManager; import me.zhanghai.android.douya.broadcast.content.RebroadcastBroadcastManager; import me.zhanghai.android.douya.broadcast.content.SendBroadcastCommentManager; import me.zhanghai.android.douya.eventbus.BroadcastCommentSendErrorEvent; import me.zhanghai.android.douya.eventbus.BroadcastCommentSentEvent; import me.zhanghai.android.douya.eventbus.EventBusUtils; import me.zhanghai.android.douya.network.api.ApiError; import me.zhanghai.android.douya.network.api.info.apiv2.Broadcast; import me.zhanghai.android.douya.network.api.info.apiv2.Comment; import me.zhanghai.android.douya.ui.ClickableSimpleAdapter; import me.zhanghai.android.douya.ui.LoadMoreAdapter; import me.zhanghai.android.douya.ui.NoChangeAnimationItemAnimator; import me.zhanghai.android.douya.ui.OnVerticalScrollListener; import me.zhanghai.android.douya.util.CheatSheetUtils; import me.zhanghai.android.douya.util.ClipboardUtils; import me.zhanghai.android.douya.util.DoubanUtils; import me.zhanghai.android.douya.util.FragmentUtils; import me.zhanghai.android.douya.util.ImeUtils; import me.zhanghai.android.douya.util.LogUtils; import me.zhanghai.android.douya.util.ToastUtils; import me.zhanghai.android.douya.util.TransitionUtils; import me.zhanghai.android.douya.util.ViewUtils; public class BroadcastFragment extends Fragment implements BroadcastAndCommentListResource.Listener, SingleBroadcastAdapter.Listener, CommentActionDialogFragment.Listener, ConfirmDeleteCommentDialogFragment.Listener, ConfirmDeleteBroadcastDialogFragment.Listener { private static final String KEY_PREFIX = BroadcastFragment.class.getName() + '.'; private static final String EXTRA_BROADCAST_ID = KEY_PREFIX + "broadcast_id"; private static final String EXTRA_BROADCAST = KEY_PREFIX + "broadcast"; private static final String EXTRA_SHOW_SEND_COMMENT = KEY_PREFIX + "show_send_comment"; private static final String EXTRA_TITLE = KEY_PREFIX + "title"; @BindView(R.id.container) FrameLayout mContainerLayout; @BindView(R.id.toolbar) Toolbar mToolbar; @BindView(R.id.shared) View mSharedView; @BindView(R.id.swipe_refresh) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.broadcast_comment_list) RecyclerView mBroadcastCommentList; @BindView(R.id.progress) ProgressBar mProgress; @BindView(R.id.comment) EditText mCommentEdit; @BindView(R.id.send) ImageButton mSendButton; private Menu mMenu; private long mBroadcastId; private Broadcast mBroadcast; private boolean mShowSendComment; private String mTitle; private BroadcastAndCommentListResource mBroadcastAndCommentListResource; private SingleBroadcastAdapter mBroadcastAdapter; private CommentAdapter mCommentAdapter; private LoadMoreAdapter mAdapter; public static BroadcastFragment newInstance(long broadcastId, Broadcast broadcast, boolean showSendComment, String title) { //noinspection deprecation BroadcastFragment fragment = new BroadcastFragment(); Bundle arguments = FragmentUtils.ensureArguments(fragment); arguments.putLong(EXTRA_BROADCAST_ID, broadcastId); arguments.putParcelable(EXTRA_BROADCAST, broadcast); arguments.putBoolean(EXTRA_SHOW_SEND_COMMENT, showSendComment); arguments.putString(EXTRA_TITLE, title); return fragment; } /** * @deprecated Use {@link #newInstance(long, Broadcast, boolean, String)} instead. */ public BroadcastFragment() {} @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle arguments = getArguments(); mBroadcast = arguments.getParcelable(EXTRA_BROADCAST); if (mBroadcast != null) { mBroadcastId = mBroadcast.id; } else { mBroadcastId = arguments.getLong(EXTRA_BROADCAST_ID); } mShowSendComment = arguments.getBoolean(EXTRA_SHOW_SEND_COMMENT); mTitle = arguments.getString(EXTRA_TITLE); setHasOptionsMenu(true); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.broadcast_fragment, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); ButterKnife.bind(this, view); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); CustomTabsHelperFragment.attachTo(this); mBroadcastAndCommentListResource = BroadcastAndCommentListResource.attachTo(mBroadcastId, mBroadcast, this); AppCompatActivity activity = (AppCompatActivity) getActivity(); activity.setTitle(getTitle()); activity.setSupportActionBar(mToolbar); mContainerLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { ActivityCompat.finishAfterTransition(getActivity()); } }); ViewCompat.setTransitionName(mSharedView, Broadcast.makeTransitionName(mBroadcastId)); // This magically gives better visual effect when the broadcast is partially visible. Using // setEnterSharedElementCallback() disables this hack when no transition is used to start // this Activity. ActivityCompat.setEnterSharedElementCallback(activity, new SharedElementCallback() { @Override public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) { mBroadcastCommentList.scrollToPosition(0); } }); mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { mBroadcastAndCommentListResource.loadBroadcast(); mBroadcastAndCommentListResource.loadCommentList(false); } }); mBroadcastCommentList.setHasFixedSize(true); mBroadcastCommentList.setItemAnimator(new NoChangeAnimationItemAnimator()); mBroadcastCommentList.setLayoutManager(new LinearLayoutManager(activity)); mBroadcastAdapter = new SingleBroadcastAdapter(null, this); setBroadcast(mBroadcastAndCommentListResource.getBroadcast()); mCommentAdapter = new CommentAdapter(mBroadcastAndCommentListResource.getCommentList(), new ClickableSimpleAdapter.OnItemClickListener<Comment, CommentAdapter.ViewHolder>() { @Override public void onItemClick(RecyclerView parent, Comment item, CommentAdapter.ViewHolder holder) { onShowCommentAction(item); } }); mAdapter = new LoadMoreAdapter(R.layout.load_more_item, mBroadcastAdapter, mCommentAdapter); mBroadcastCommentList.setAdapter(mAdapter); mBroadcastCommentList.addOnScrollListener(new OnVerticalScrollListener() { public void onScrolledToBottom() { mBroadcastAndCommentListResource.loadCommentList(true); } }); CheatSheetUtils.setup(mSendButton); mSendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onSendComment(); } }); updateSendCommentStatus(); if (savedInstanceState == null) { if (mShowSendComment) { TransitionUtils.postAfterTransition(this, new Runnable() { @Override public void run() { onShowSendComment(); } }); } } TransitionUtils.setEnterReturnExplode(this); TransitionUtils.setupTransitionOnActivityCreated(this); } @Override public void onStart() { super.onStart(); EventBusUtils.register(this); } @Override public void onStop() { super.onStop(); EventBusUtils.unregister(this); } @Override public void onDestroy() { super.onDestroy(); mBroadcastAndCommentListResource.detach(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.broadcast, menu); mMenu = menu; } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); updateOptionsMenu(); } private void updateOptionsMenu() { if (mMenu == null) { return; } Broadcast broadcast = mBroadcastAndCommentListResource.getBroadcast(); boolean hasBroadcast = broadcast != null; mMenu.findItem(R.id.action_copy_text).setVisible(hasBroadcast); boolean canDelete = hasBroadcast && broadcast.isAuthorOneself(getActivity()); mMenu.findItem(R.id.action_delete).setVisible(canDelete); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: ActivityCompat.finishAfterTransition(getActivity()); return true; case R.id.action_copy_text: copyText(); return true; case R.id.action_delete: onDeleteBroadcast(); return true; default: return super.onOptionsItemSelected(item); } } private String getTitle() { return !TextUtils.isEmpty(mTitle) ? mTitle : getString(R.string.broadcast_title); } @Override public void onLoadBroadcastStarted(int requestCode) { updateRefreshing(); } @Override public void onLoadBroadcastFinished(int requestCode) { updateRefreshing(); } @Override public void onLoadBroadcastError(int requestCode, VolleyError error) { LogUtils.e(error.toString()); Activity activity = getActivity(); ToastUtils.show(ApiError.getErrorString(error, activity), activity); } @Override public void onBroadcastChanged(int requestCode, Broadcast newBroadcast) { setBroadcast(newBroadcast); } @Override public void onBroadcastRemoved(int requestCode) { getActivity().finish(); } @Override public void onBroadcastWriteStarted(int requestCode) { mBroadcastAdapter.notifyBroadcastChanged(); } @Override public void onBroadcastWriteFinished(int requestCode) { mBroadcastAdapter.notifyBroadcastChanged(); } private void setBroadcast(Broadcast broadcast) { mBroadcastAdapter.setBroadcast(broadcast); updateOptionsMenu(); updateSendCommentStatus(); } @Override public void onLoadCommentListStarted(int requestCode) { updateRefreshing(); } @Override public void onLoadCommentListFinished(int requestCode) { updateRefreshing(); } @Override public void onLoadCommentListError(int requestCode, VolleyError error) { LogUtils.e(error.toString()); Activity activity = getActivity(); ToastUtils.show(ApiError.getErrorString(error, activity), activity); } @Override public void onCommentListChanged(int requestCode, List<Comment> newCommentList) { mCommentAdapter.replace(newCommentList); BroadcastCommentCountFixer.onCommentListChanged( mBroadcastAndCommentListResource.getBroadcast(), mBroadcastAndCommentListResource.getCommentList(), this); } @Override public void onCommentListAppended(int requestCode, List<Comment> appendedCommentList) { mCommentAdapter.addAll(appendedCommentList); BroadcastCommentCountFixer.onCommentListChanged( mBroadcastAndCommentListResource.getBroadcast(), mBroadcastAndCommentListResource.getCommentList(), this); } @Override public void onCommentRemoved(int requestCode, int position) { mCommentAdapter.remove(position); BroadcastCommentCountFixer.onCommentRemoved( mBroadcastAndCommentListResource.getBroadcast(), this); } private void updateRefreshing() { boolean loadingBroadcast = mBroadcastAndCommentListResource.isLoadingBroadcast(); boolean hasBroadcast = mBroadcastAndCommentListResource.hasBroadcast(); boolean loadingCommentList = mBroadcastAndCommentListResource.isLoadingCommentList(); mSwipeRefreshLayout.setRefreshing(loadingBroadcast && (mSwipeRefreshLayout.isRefreshing() || hasBroadcast)); ViewUtils.setVisibleOrGone(mProgress, loadingBroadcast && !hasBroadcast); mAdapter.setProgressVisible(hasBroadcast && loadingCommentList); } @Override public void onLike(Broadcast broadcast, boolean like) { LikeBroadcastManager.getInstance().write(broadcast, like, getActivity()); } @Override public void onRebroadcast(Broadcast broadcast, boolean rebroadcast) { RebroadcastBroadcastManager.getInstance().write(broadcast, rebroadcast, getActivity()); } @Override public void onComment(Broadcast broadcast) { onShowSendComment(); } @Override public void onViewActivity(Broadcast broadcast) { BroadcastActivityDialogFragment.show(broadcast, this); } private void onShowCommentAction(Comment comment) { boolean canReplyTo = canSendComment(); Activity activity = getActivity(); boolean canDelete = (mBroadcastAdapter.hasBroadcast() && mBroadcastAdapter.getBroadcast().isAuthorOneself(activity)) || comment.isAuthorOneself(activity); CommentActionDialogFragment.show(comment, canReplyTo, canDelete, this); } @Override public void onReplyToComment(Comment comment) { mCommentEdit.getText().replace(mCommentEdit.getSelectionStart(), mCommentEdit.getSelectionEnd(), DoubanUtils.getAtUserString(comment.author)); onShowSendComment(); } @Override public void onCopyCommentText(Comment comment) { Activity activity = getActivity(); ClipboardUtils.copyText(comment.getClipboardLabel(), comment.getClipboardText(activity), activity); } @Override public void onDeleteComment(Comment comment) { ConfirmDeleteCommentDialogFragment.show(comment, this); } @Override public void deleteComment(Comment comment) { DeleteBroadcastCommentManager.getInstance().write( mBroadcastAndCommentListResource.getBroadcastId(), comment.id, getActivity()); } private void onShowSendComment() { if (canSendComment()) { ImeUtils.showIme(mCommentEdit); } else { ToastUtils.show(R.string.broadcast_send_comment_disabled, getActivity()); } } private void onSendComment() { String comment = mCommentEdit.getText().toString(); if (TextUtils.isEmpty(comment)) { ToastUtils.show(R.string.broadcast_send_comment_error_empty, getActivity()); return; } sendComment(comment); } private void sendComment(String comment) { SendBroadcastCommentManager.getInstance().write( mBroadcastAndCommentListResource.getBroadcastId(), comment, getActivity()); updateSendCommentStatus(); } @Subscribe(threadMode = ThreadMode.MAIN) public void onBroadcastCommentSent(BroadcastCommentSentEvent event) { if (event.isFromMyself(this)) { return; } if (event.broadcastId == mBroadcastAndCommentListResource.getBroadcastId()) { mBroadcastCommentList.scrollToPosition(mAdapter.getItemCount() - 1); mCommentEdit.setText(null); updateSendCommentStatus(); } } @Subscribe(threadMode = ThreadMode.MAIN) public void onBroadcastCommentSendError(BroadcastCommentSendErrorEvent event) { if (event.isFromMyself(this)) { return; } if (event.broadcastId == mBroadcastAndCommentListResource.getBroadcastId()) { updateSendCommentStatus(); } } private boolean canSendComment() { Broadcast broadcast = mBroadcastAndCommentListResource.getBroadcast(); return broadcast != null && broadcast.canComment(); } private void updateSendCommentStatus() { boolean canSendComment = canSendComment(); SendBroadcastCommentManager manager = SendBroadcastCommentManager.getInstance(); long broadcastId = mBroadcastAndCommentListResource.getBroadcastId(); boolean sendingComment = manager.isWriting(broadcastId); boolean enabled = canSendComment && !sendingComment; mCommentEdit.setEnabled(enabled); mSendButton.setEnabled(enabled); boolean hasBroadcast = mBroadcastAndCommentListResource.hasBroadcast(); mCommentEdit.setHint(!hasBroadcast || canSendComment ? R.string.broadcast_send_comment_hint : R.string.broadcast_send_comment_hint_disabled); if (sendingComment) { mCommentEdit.setText(manager.getComment(broadcastId)); } } private void copyText() { Broadcast broadcast = mBroadcastAdapter.getBroadcast(); Activity activity = getActivity(); if (broadcast == null) { ToastUtils.show(R.string.broadcast_copy_text_not_loaded, activity); return; } ClipboardUtils.copyText(broadcast.getClipboradLabel(), broadcast.getClipboardText(activity), activity); } private void onDeleteBroadcast() { ConfirmDeleteBroadcastDialogFragment.show(this); } @Override public void deleteBroadcast() { DeleteBroadcastManager.getInstance().write( mBroadcastAndCommentListResource.getBroadcastId(), getActivity()); } }