Twidere-App-Android-Twitter.../twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java

454 lines
18 KiB
Java
Raw Normal View History

package org.mariotaku.twidere.fragment.support;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
2015-03-08 11:35:05 +01:00
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
2015-04-16 22:29:36 +02:00
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter.StatusAdapterListener;
import org.mariotaku.twidere.app.TwidereApplication;
2015-01-10 16:17:02 +01:00
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.ShortcutCallback;
import org.mariotaku.twidere.util.ReadStateManager;
import org.mariotaku.twidere.util.RecyclerViewUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.message.StatusListChangedEvent;
import org.mariotaku.twidere.view.holder.GapViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
2015-03-04 09:02:32 +01:00
import edu.tsinghua.spice.Utilies.SpiceProfilingUtil;
2015-03-29 13:06:55 +02:00
import edu.tsinghua.spice.Utilies.TypeMappingUtil;
2015-03-04 09:02:32 +01:00
2015-01-14 09:47:51 +01:00
import static org.mariotaku.twidere.util.Utils.setMenuForStatus;
/**
* Created by mariotaku on 14/11/5.
*/
2015-04-16 14:48:24 +02:00
public abstract class AbsStatusesFragment<Data> extends AbsContentListFragment<AbsStatusesAdapter<Data>>
implements LoaderCallbacks<Data>, StatusAdapterListener, ShortcutCallback {
2015-01-14 09:47:51 +01:00
private final Object mStatusesBusCallback;
private SharedPreferences mPreferences;
2015-01-14 09:47:51 +01:00
private PopupMenu mPopupMenu;
private ReadStateManager mReadStateManager;
private KeyboardShortcutsHandler mKeyboardShortcutsHandler;
private ParcelableStatus mSelectedStatus;
private OnMenuItemClickListener mOnStatusMenuItemClickListener = new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
final ParcelableStatus status = mSelectedStatus;
if (status == null) return false;
if (item.getItemId() == MENU_SHARE) {
final Intent shareIntent = Utils.createStatusShareIntent(getActivity(), status);
startActivity(Intent.createChooser(shareIntent, getString(R.string.share_status)));
return true;
}
return Utils.handleMenuItemClick(getActivity(), AbsStatusesFragment.this,
getFragmentManager(), getTwitterWrapper(), status, item);
}
};
2015-04-16 14:48:24 +02:00
private int mPositionBackup;
2015-01-10 16:17:02 +01:00
protected AbsStatusesFragment() {
mStatusesBusCallback = createMessageBusCallback();
}
2015-04-16 14:48:24 +02:00
public SharedPreferences getSharedPreferences() {
if (mPreferences != null) return mPreferences;
return mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
}
2015-04-16 14:48:24 +02:00
public abstract int getStatuses(long[] accountIds, long[] maxIds, long[] sinceIds);
@Override
public boolean handleKeyboardShortcutSingle(int keyCode, @NonNull KeyEvent event) {
if (!KeyboardShortcutsHandler.isValidForHotkey(keyCode, event)) return false;
String action = mKeyboardShortcutsHandler.getKeyAction("navigation", keyCode, event);
if ("navigation.refresh".equals(action)) {
triggerRefresh();
return true;
}
2015-04-16 14:48:24 +02:00
final RecyclerView mRecyclerView = getRecyclerView();
final LinearLayoutManager layoutManager = getLayoutManager();
final View focusedChild = RecyclerViewUtils.findRecyclerViewChild(mRecyclerView, layoutManager.getFocusedChild());
final int position;
if (focusedChild != null && focusedChild.getParent() == mRecyclerView) {
position = mRecyclerView.getChildLayoutPosition(focusedChild);
} else {
return false;
}
if (position == -1) return false;
2015-04-16 14:48:24 +02:00
final ParcelableStatus status = getAdapter().getStatus(position);
if (status == null) return false;
if (action == null) {
action = mKeyboardShortcutsHandler.getKeyAction("status", keyCode, event);
}
if (action == null) return false;
switch (action) {
case "status.reply": {
final Intent intent = new Intent(INTENT_ACTION_REPLY);
intent.putExtra(EXTRA_STATUS, status);
startActivity(intent);
return true;
}
case "status.retweet": {
RetweetQuoteDialogFragment.show(getFragmentManager(), status);
return true;
}
case "status.favorite": {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
}
return true;
}
}
return false;
}
@Override
2015-04-16 14:48:24 +02:00
public boolean handleKeyboardShortcutRepeat(int keyCode, int repeatCount, @NonNull KeyEvent event) {
if (!KeyboardShortcutsHandler.isValidForHotkey(keyCode, event)) return false;
String action = mKeyboardShortcutsHandler.getKeyAction("navigation", keyCode, event);
final LinearLayoutManager layoutManager = getLayoutManager();
final RecyclerView recyclerView = getRecyclerView();
final View focusedChild = RecyclerViewUtils.findRecyclerViewChild(recyclerView, layoutManager.getFocusedChild());
final int position;
if (focusedChild != null) {
position = recyclerView.getChildLayoutPosition(focusedChild);
} else if (layoutManager.findFirstVisibleItemPosition() == 0) {
position = -1;
} else {
final int itemCount = getAdapter().getItemCount();
if (layoutManager.findLastVisibleItemPosition() == itemCount - 1) {
position = itemCount;
} else {
position = mPositionBackup;
}
}
2015-04-16 14:48:24 +02:00
mPositionBackup = position;
if (action != null) {
switch (action) {
case "navigation.previous": {
RecyclerViewUtils.focusNavigate(recyclerView, layoutManager, position, -1);
return true;
}
case "navigation.next": {
RecyclerViewUtils.focusNavigate(recyclerView, layoutManager, position, 1);
return true;
}
}
2015-01-17 21:19:11 +01:00
}
2015-04-16 14:48:24 +02:00
return false;
}
2015-03-12 10:43:28 +01:00
@Override
public final Loader<Data> onCreateLoader(int id, Bundle args) {
final boolean fromUser = args.getBoolean(EXTRA_FROM_USER);
args.remove(EXTRA_FROM_USER);
return onCreateStatusesLoader(getActivity(), args, fromUser);
}
@Override
public final void onLoadFinished(Loader<Data> loader, Data data) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
2015-03-12 10:43:28 +01:00
final SharedPreferences preferences = getSharedPreferences();
final boolean rememberPosition = preferences.getBoolean(KEY_REMEMBER_POSITION, false);
2015-03-12 10:43:28 +01:00
final boolean readFromBottom = preferences.getBoolean(KEY_READ_FROM_BOTTOM, false);
final long lastReadId;
final int lastVisiblePos, lastVisibleTop;
final String tag = getCurrentReadPositionTag();
2015-04-16 14:48:24 +02:00
final LinearLayoutManager mLayoutManager = getLayoutManager();
2015-03-12 10:43:28 +01:00
if (readFromBottom) {
lastVisiblePos = mLayoutManager.findLastVisibleItemPosition();
} else {
lastVisiblePos = mLayoutManager.findFirstVisibleItemPosition();
}
2015-04-08 11:47:34 +02:00
if (lastVisiblePos != RecyclerView.NO_POSITION) {
2015-04-16 14:48:24 +02:00
lastReadId = adapter.getStatusId(lastVisiblePos);
2015-04-08 11:47:34 +02:00
final View positionView = mLayoutManager.findViewByPosition(lastVisiblePos);
lastVisibleTop = positionView != null ? positionView.getTop() : 0;
} else if (rememberPosition && tag != null) {
lastReadId = mReadStateManager.getPosition(tag);
lastVisibleTop = 0;
2015-03-12 10:43:28 +01:00
} else {
lastReadId = -1;
lastVisibleTop = 0;
}
2015-04-16 14:48:24 +02:00
adapter.setData(data);
if (!(loader instanceof IExtendedLoader) || ((IExtendedLoader) loader).isFromUser()) {
2015-04-16 14:48:24 +02:00
adapter.setLoadMoreSupported(hasMoreData(data));
2015-04-08 11:47:34 +02:00
setRefreshEnabled(true);
2015-03-12 10:43:28 +01:00
int pos = -1;
2015-04-16 14:48:24 +02:00
for (int i = 0, j = adapter.getItemCount(); i < j; i++) {
if (lastReadId != -1 && lastReadId == adapter.getStatusId(i)) {
2015-03-12 10:43:28 +01:00
pos = i;
break;
}
}
2015-04-16 14:48:24 +02:00
if (pos != -1 && adapter.isStatus(pos) && (readFromBottom || lastVisiblePos != 0)) {
mLayoutManager.scrollToPositionWithOffset(pos, lastVisibleTop);
2015-03-12 10:43:28 +01:00
}
}
if (loader instanceof IExtendedLoader) {
((IExtendedLoader) loader).setFromUser(false);
2015-03-12 10:43:28 +01:00
}
setListShown(true);
onLoadingFinished();
}
2015-03-12 10:43:28 +01:00
@Override
public void onLoaderReset(Loader<Data> loader) {
if (loader instanceof IExtendedLoader) {
((IExtendedLoader) loader).setFromUser(false);
}
2015-03-12 10:43:28 +01:00
}
@Override
2015-01-10 16:17:02 +01:00
public void onGapClick(GapViewHolder holder, int position) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
final ParcelableStatus status = adapter.getStatus(position);
final long sinceId = position + 1 < adapter.getStatusesCount() ? adapter.getStatus(position + 1).id : -1;
2015-01-10 16:17:02 +01:00
final long[] accountIds = {status.account_id};
final long[] maxIds = {status.id};
final long[] sinceIds = {sinceId};
getStatuses(accountIds, maxIds, sinceIds);
}
2015-04-16 14:48:24 +02:00
@Override
public void onMediaClick(StatusViewHolder holder, ParcelableMedia media, int position) {
final AbsStatusesAdapter<Data> adapter = getAdapter();
final ParcelableStatus status = adapter.getStatus(position);
if (status == null) return;
//TODO open media animation
Bundle options = null;
Utils.openMedia(getActivity(), status, media, options);
2015-04-16 14:48:24 +02:00
//spice
SpiceProfilingUtil.log(getActivity(),
status.id + ",Clicked," + status.account_id + "," + status.user_id + "," + status.text_plain.length()
+ "," + media.media_url + "," + TypeMappingUtil.getMediaType(media.type)
+ "," + adapter.isMediaPreviewEnabled() + "," + status.timestamp);
SpiceProfilingUtil.profile(getActivity(), status.account_id,
status.id + ",Clicked," + status.account_id + "," + status.user_id + "," + status.text_plain.length()
+ "," + media.media_url + "," + TypeMappingUtil.getMediaType(media.type)
+ "," + adapter.isMediaPreviewEnabled() + "," + status.timestamp);
//end
}
@Override
public void onStatusActionClick(StatusViewHolder holder, int id, int position) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
final ParcelableStatus status = adapter.getStatus(position);
if (status == null) return;
final FragmentActivity activity = getActivity();
switch (id) {
case R.id.reply_count: {
2015-01-01 11:38:34 +01:00
final Intent intent = new Intent(INTENT_ACTION_REPLY);
intent.setPackage(activity.getPackageName());
2015-01-01 11:38:34 +01:00
intent.putExtra(EXTRA_STATUS, status);
activity.startActivity(intent);
break;
}
case R.id.retweet_count: {
RetweetQuoteDialogFragment.show(getFragmentManager(), status);
break;
}
case R.id.favorite_count: {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null) return;
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
}
break;
}
}
}
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
Utils.openStatus(getActivity(), adapter.getStatus(position), null);
}
@Override
public boolean onStatusLongClick(StatusViewHolder holder, int position) {
//TODO handle long click event
return true;
}
@Override
2015-01-14 09:47:51 +01:00
public void onStatusMenuClick(StatusViewHolder holder, View menuView, int position) {
if (mPopupMenu != null) {
mPopupMenu.dismiss();
}
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
final PopupMenu popupMenu = new PopupMenu(adapter.getContext(), menuView,
Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0);
popupMenu.setOnMenuItemClickListener(mOnStatusMenuItemClickListener);
2015-01-14 09:47:51 +01:00
popupMenu.inflate(R.menu.action_status);
2015-04-16 14:48:24 +02:00
final ParcelableStatus status = adapter.getStatus(position);
setMenuForStatus(adapter.getContext(), popupMenu.getMenu(), status);
2015-01-14 09:47:51 +01:00
popupMenu.show();
mPopupMenu = popupMenu;
mSelectedStatus = status;
}
2015-04-16 22:29:36 +02:00
@Override
public void onUserProfileClick(StatusViewHolder holder, ParcelableStatus status, int position) {
final FragmentActivity activity = getActivity();
final View profileImageView = holder.getProfileImageView();
final View profileTypeView = holder.getProfileTypeView();
final Bundle options = Utils.makeSceneTransitionOption(activity,
new Pair<>(profileImageView, UserFragment.TRANSITION_NAME_PROFILE_IMAGE),
new Pair<>(profileTypeView, UserFragment.TRANSITION_NAME_PROFILE_TYPE));
2015-04-17 09:17:22 +02:00
if (status.is_quote) {
Utils.openUserProfile(activity, status.account_id, status.quoted_by_user_id,
status.quoted_by_user_screen_name, options);
} else {
Utils.openUserProfile(activity, status.account_id, status.user_id, status.user_screen_name, options);
}
2015-04-16 22:29:36 +02:00
}
2015-01-10 16:17:02 +01:00
@Override
2015-04-16 14:48:24 +02:00
public void onStart() {
super.onStart();
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.register(mStatusesBusCallback);
2015-01-10 16:17:02 +01:00
}
@Override
2015-04-16 14:48:24 +02:00
public void onStop() {
final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus();
bus.unregister(mStatusesBusCallback);
super.onStop();
}
@Override
public void onDestroyView() {
if (mPopupMenu != null) {
mPopupMenu.dismiss();
}
super.onDestroyView();
}
2015-04-16 22:29:36 +02:00
@Override
public final boolean scrollToStart() {
saveReadPosition();
return super.scrollToStart();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mReadStateManager = getReadStateManager();
final FragmentActivity activity = getActivity();
final TwidereApplication application = TwidereApplication.getInstance(activity);
mKeyboardShortcutsHandler = application.getKeyboardShortcutsHandler();
getAdapter().setListener(this);
getScrollListener().setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
saveReadPosition();
}
}
});
final Bundle loaderArgs = new Bundle(getArguments());
loaderArgs.putBoolean(EXTRA_FROM_USER, true);
getLoaderManager().initLoader(0, loaderArgs, this);
setListShown(false);
}
2015-01-10 16:17:02 +01:00
protected Object createMessageBusCallback() {
return new StatusesBusCallback();
}
protected abstract long[] getAccountIds();
protected Data getAdapterData() {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
return adapter.getData();
}
protected void setAdapterData(Data data) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
adapter.setData(data);
}
protected String getReadPositionTag() {
return null;
}
2015-01-10 16:17:02 +01:00
protected abstract boolean hasMoreData(Data data);
2015-04-16 22:29:36 +02:00
protected abstract Loader<Data> onCreateStatusesLoader(final Context context, final Bundle args,
final boolean fromUser);
2015-04-16 14:48:24 +02:00
protected abstract void onLoadingFinished();
protected void saveReadPosition() {
final String readPositionTag = getReadPositionTagWithAccounts();
if (readPositionTag == null) return;
2015-04-16 14:48:24 +02:00
final LinearLayoutManager layoutManager = getLayoutManager();
final int position = layoutManager.findFirstVisibleItemPosition();
if (position == RecyclerView.NO_POSITION) return;
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
final ParcelableStatus status = adapter.getStatus(position);
if (status == null) return;
mReadStateManager.setPosition(readPositionTag, status.id);
mReadStateManager.setPosition(getCurrentReadPositionTag(), status.id, true);
}
private String getCurrentReadPositionTag() {
final String tag = getReadPositionTagWithAccounts();
if (tag == null) return null;
return tag + "_current";
2015-03-12 10:43:28 +01:00
}
private String getReadPositionTagWithAccounts() {
return Utils.getReadPositionTagWithAccounts(getReadPositionTag(), getAccountIds());
}
protected final class StatusesBusCallback {
protected StatusesBusCallback() {
}
@Subscribe
public void notifyStatusListChanged(StatusListChangedEvent event) {
2015-04-16 14:48:24 +02:00
final AbsStatusesAdapter<Data> adapter = getAdapter();
adapter.notifyDataSetChanged();
}
}
}