/* * Copyright 2015 Hippo Seven * * 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 com.hippo.nimingban.ui; import android.content.Intent; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.text.TextUtils; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager; import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemAdapter; import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemConstants; import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAction; import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionRemoveItem; import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager; import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractSwipeableItemViewHolder; import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; import com.hippo.conaco.DataContainer; import com.hippo.conaco.ProgressNotify; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.MarginItemDecoration; import com.hippo.easyrecyclerview.RawMarginItemDecoration; import com.hippo.effect.ViewTransition; import com.hippo.io.FileInputStreamPipe; import com.hippo.nimingban.Constants; import com.hippo.nimingban.R; import com.hippo.nimingban.client.data.ACSite; import com.hippo.nimingban.dao.ACRecordRaw; import com.hippo.nimingban.util.DB; import com.hippo.nimingban.util.ReadableTime; import com.hippo.nimingban.util.Settings; import com.hippo.nimingban.widget.FontTextView; import com.hippo.nimingban.widget.LoadImageView; import com.hippo.ripple.Ripple; import com.hippo.util.DrawableManager; import com.hippo.widget.Snackbar; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.Messenger; import com.hippo.yorozuya.ResourcesUtils; import com.hippo.yorozuya.io.InputStreamPipe; import java.io.File; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.HashSet; import java.util.Set; import de.greenrobot.dao.query.LazyList; import jp.wasabeef.recyclerview.animators.SlideInUpAnimator; public final class RecordActivity extends TranslucentActivity implements EasyRecyclerView.OnItemClickListener { private LazyList<ACRecordRaw> mLazyList; private EasyRecyclerView mRecyclerView; private ViewTransition mViewTransition; private RecyclerView.Adapter mAdapter; private RecyclerView.Adapter mWrappedAdapter; private RecyclerViewSwipeManager mRecyclerViewSwipeManager; private RecyclerViewTouchActionGuardManager mRecyclerViewTouchActionGuardManager; private Set<WeakReference<LoadImageView>> mLoadImageViewSet = new HashSet<>(); @Override protected int getLightThemeResId() { return Settings.getColorStatusBar() ? R.style.NormalActivity : R.style.NormalActivity_NoStatus; } @Override protected int getDarkThemeResId() { return Settings.getColorStatusBar() ? R.style.NormalActivity_Dark : R.style.NormalActivity_Dark_NoStatus; } @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark)); ToolbarActivityHelper.setContentView(this, R.layout.activity_record); setActionBarUpIndicator(DrawableManager.getDrawable(this, R.drawable.v_arrow_left_dark_x24)); View tip = findViewById(R.id.tip); mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view); mViewTransition = new ViewTransition(tip, mRecyclerView); // Layout Manager int interval = getResources().getDimensionPixelOffset(R.dimen.card_interval); if (getResources().getBoolean(R.bool.two_way)) { mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); MarginItemDecoration decoration = new MarginItemDecoration(interval, interval, interval, interval, interval); mRecyclerView.addItemDecoration(decoration); decoration.applyPaddings(mRecyclerView); mRecyclerView.setItemAnimator(new SlideInUpAnimator()); } else { int halfInterval = interval / 2; mRecyclerView.addItemDecoration(new RawMarginItemDecoration(0, halfInterval, 0, halfInterval)); mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mRecyclerView.setPadding(0, halfInterval, 0, halfInterval); } // touch guard manager (this class is required to suppress scrolling while swipe-dismiss animation is running) mRecyclerViewTouchActionGuardManager = new RecyclerViewTouchActionGuardManager(); mRecyclerViewTouchActionGuardManager.setInterceptVerticalScrollingWhileAnimationRunning(true); mRecyclerViewTouchActionGuardManager.setEnabled(true); // swipe manager mRecyclerViewSwipeManager = new RecyclerViewSwipeManager(); mAdapter = new RecordAdapter(); mAdapter.setHasStableIds(true); mWrappedAdapter = mRecyclerViewSwipeManager.createWrappedAdapter(mAdapter); // wrap for swiping final GeneralItemAnimator animator = new SwipeDismissItemAnimator(); // Change animations are enabled by default since support-v7-recyclerview v22. // Disable the change animation in order to make turning back animation of swiped item works properly. animator.setSupportsChangeAnimations(false); mRecyclerView.hasFixedSize(); mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter mRecyclerView.setItemAnimator(animator); mRecyclerView.setOnItemClickListener(this); mRecyclerView.setSelector(Ripple.generateRippleDrawable(this, ResourcesUtils.getAttrBoolean(this, R.attr.dark))); mRecyclerView.setDrawSelectorOnTop(true); mRecyclerView.setClipToPadding(false); mRecyclerView.setClipChildren(false); // NOTE: // The initialization order is very important! This order determines the priority of touch event handling. // // priority: TouchActionGuard > Swipe > DragAndDrop mRecyclerViewTouchActionGuardManager.attachRecyclerView(mRecyclerView); mRecyclerViewSwipeManager.attachRecyclerView(mRecyclerView); updateLazyList(); checkEmpty(false); Messenger.getInstance().register(Constants.MESSENGER_ID_UPDATE_RECORD, this); } @Override protected void onDestroy() { super.onDestroy(); Messenger.getInstance().unregister(Constants.MESSENGER_ID_UPDATE_RECORD, this); if (mRecyclerViewSwipeManager != null) { mRecyclerViewSwipeManager.release(); mRecyclerViewSwipeManager = null; } if (mRecyclerViewTouchActionGuardManager != null) { mRecyclerViewTouchActionGuardManager.release(); mRecyclerViewTouchActionGuardManager = null; } if (mRecyclerView != null) { mRecyclerView.setItemAnimator(null); mRecyclerView.setAdapter(null); mRecyclerView = null; } if (mWrappedAdapter != null) { WrapperAdapterUtils.releaseAll(mWrappedAdapter); mWrappedAdapter = null; } mAdapter = null; if (mLazyList != null) { mLazyList.close(); } for (WeakReference<LoadImageView> ref : mLoadImageViewSet) { LoadImageView liv = ref.get(); if (liv != null) { liv.unload(); } } mLoadImageViewSet.clear(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onReceive(final int id, final Object obj) { if (id == Constants.MESSENGER_ID_UPDATE_RECORD) { updateLazyList(); mAdapter.notifyDataSetChanged(); checkEmpty(true); } else { super.onReceive(id, obj); } } // Remember to notify private void updateLazyList() { LazyList<ACRecordRaw> lazyList = DB.getACRecordLazyList(); if (mLazyList != null) { mLazyList.close(); } mLazyList = lazyList; } private void checkEmpty(boolean animation) { if (mAdapter.getItemCount() == 0) { mViewTransition.showView(0, animation); } else { mViewTransition.showView(1, animation); } } @Override public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { ACRecordRaw raw = mLazyList.get(position); String postId = raw.getPostid(); if (!TextUtils.isEmpty(postId)) { Intent intent = new Intent(this, PostActivity.class); intent.setAction(PostActivity.ACTION_SITE_ID); intent.putExtra(PostActivity.KEY_SITE, ACSite.getInstance().getId()); intent.putExtra(PostActivity.KEY_ID, postId); startActivity(intent); return true; } else { return false; } } private class RecordHolder extends AbstractSwipeableItemViewHolder { public View swipable; public TextView leftText; public TextView rightText; private FontTextView content; private LoadImageView thumb; public RecordHolder(View itemView) { super(itemView); swipable = itemView.findViewById(R.id.swipable); leftText = (TextView) itemView.findViewById(R.id.left_text); rightText = (TextView) itemView.findViewById(R.id.right_text); content = (FontTextView) itemView.findViewById(R.id.content); thumb = (LoadImageView) itemView.findViewById(R.id.thumb); } @Override public View getSwipeableContainerView() { return swipable; } } private class RecordAdapter extends RecyclerView.Adapter<RecordHolder> implements SwipeableItemAdapter<RecordHolder> { @Override public RecordHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecordHolder holder = new RecordHolder(getLayoutInflater().inflate(R.layout.item_record, parent, false)); mLoadImageViewSet.add(new WeakReference<>(holder.thumb)); return holder; } @Override public void onBindViewHolder(RecordHolder holder, int position) { ACRecordRaw raw = mLazyList.get(position); String leftText = null; switch (raw.getType()) { case DB.AC_RECORD_POST: leftText = getString(R.string.create_post); break; case DB.AC_RECORD_REPLY: leftText = getString(R.string.reply); } holder.leftText.setText(leftText); holder.rightText.setText(ReadableTime.getDisplayTime(raw.getTime())); holder.content.setText(raw.getContent()); String image = raw.getImage(); if (!TextUtils.isEmpty(image)) { holder.thumb.setVisibility(View.VISIBLE); holder.thumb.unload(); holder.thumb.load(image, "dump", new LocalPathDataContain(image), false); } else { holder.thumb.setVisibility(View.GONE); holder.thumb.unload(); } holder.content.setTextSize(Settings.getFontSize()); holder.content.setLineSpacing(LayoutUtils.dp2pix(RecordActivity.this, Settings.getLineSpacing()), 1.0f); if (Settings.getFixEmojiDisplay()) { holder.content.useCustomTypeface(); } else { holder.content.useOriginalTypeface(); } } @Override public long getItemId(int position) { return mLazyList.get(position).getId(); } @Override public int getItemCount() { return mLazyList.size(); } @Override public int onGetSwipeReactionType(RecordHolder holder, int position, int x, int y) { return SwipeableItemConstants.REACTION_CAN_SWIPE_BOTH_H; } @Override public void onSetSwipeBackground(RecordHolder holder, int position, int type) { // Empty } @Override public void onSwipeItemStarted(RecordHolder holder, int position) { // Empty } @Override public SwipeResultAction onSwipeItem(RecordHolder holder, int position, int result) { switch (result) { // swipe right case SwipeableItemConstants.RESULT_SWIPED_RIGHT: case SwipeableItemConstants.RESULT_SWIPED_LEFT: return new DeleteAction(position); case SwipeableItemConstants.RESULT_CANCELED: default: return null; } } } private class DeleteAction extends SwipeResultActionRemoveItem { private int mPosition; public DeleteAction(int position) { mPosition = position; } @Override protected void onPerformAction() { super.onPerformAction(); final int position = mPosition; final ACRecordRaw raw = mLazyList.get(position); if (raw != null) { DB.removeACRecord(mLazyList.get(position)); updateLazyList(); mAdapter.notifyItemRemoved(position); checkEmpty(true); Snackbar snackbar = Snackbar.make(mRecyclerView, R.string.record_deleted, Snackbar.LENGTH_LONG); snackbar.setAction(R.string.undo, new View.OnClickListener() { @Override public void onClick(View v) { DB.addACRecord(raw.getType(), raw.getRecordid(), raw.getPostid(), raw.getContent(), raw.getImage(), raw.getTime()); updateLazyList(); mAdapter.notifyDataSetChanged(); checkEmpty(true); } }); snackbar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { if (event != DISMISS_EVENT_ACTION) { String image = raw.getImage(); if (image != null) { new File(image).delete(); } } } }); snackbar.show(); } } } private static class LocalPathDataContain implements DataContainer { private File mFile; public LocalPathDataContain(String path) { File file = new File(path); if (file.isFile()) { mFile = file; } } @Override public void onUrlMoved(String requestUrl, String responseUrl) { } @Override public boolean save(InputStream is, long length, String mediaType, ProgressNotify notify) { return false; } @Override public InputStreamPipe get() { if (mFile != null) { return new FileInputStreamPipe(mFile); } else { return null; } } @Override public void remove() { // Empty } } }