/* * Copyright (C) 2013 Chris Lacy 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.tweetlanes.android.core.view; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.view.ActionMode; 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.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.ViewSwitcher; import com.tweetlanes.android.core.App; import com.tweetlanes.android.core.Constant; import com.tweetlanes.android.core.Constant.SystemEvent; import com.tweetlanes.android.core.R; import com.tweetlanes.android.core.model.AccountDescriptor; import com.tweetlanes.android.core.model.ComposeTweetDefault; import com.tweetlanes.android.core.util.LazyImageLoader; import com.tweetlanes.android.core.widget.pulltorefresh.PullToRefreshBase.OnLastItemVisibleListener; import com.tweetlanes.android.core.widget.pulltorefresh.PullToRefreshBase.OnRefreshListener; import com.tweetlanes.android.core.widget.pulltorefresh.PullToRefreshListView; import org.tweetalib.android.TwitterConstant.UsersType; import org.tweetalib.android.TwitterContentHandle; import org.tweetalib.android.TwitterContentHandleBase; import org.tweetalib.android.TwitterFetchResult; import org.tweetalib.android.TwitterFetchUsers.FinishedCallback; import org.tweetalib.android.TwitterManager; import org.tweetalib.android.TwitterPaging; import org.tweetalib.android.model.TwitterStatus; import org.tweetalib.android.model.TwitterUser; import org.tweetalib.android.model.TwitterUsers; import java.util.ArrayList; public class UserFeedFragment extends BaseLaneFragment { /* * */ public static UserFeedFragment newInstance(int laneIndex, final TwitterContentHandleBase handleBase, final String screenName, final String laneIdentifier, final String currentAccountKey) { UserFeedFragment fragment = new UserFeedFragment(); fragment.mContentHandle = TwitterManager.get().getContentHandle( handleBase, screenName, laneIdentifier, currentAccountKey); fragment.configureBaseLaneFragment(laneIndex, fragment.mContentHandle.getTypeAsString(), new ConfigureBundleListener() { @Override public void addValues(Bundle args) { // TODO: serializing is a slow way of doing this... args.putSerializable("handleBase", handleBase); args.putString("screenName", screenName); args.putString("laneIdentifier", laneIdentifier); } }); return fragment; } private PullToRefreshListView mUserFeedListView; private UserFeedListAdapter mUserFeedListAdapter; private TwitterContentHandle mContentHandle; private TwitterUsers _mUsersFeed; private FinishedCallback mUserDataRefreshCallback; private ViewSwitcher mViewSwitcher; private final ArrayList<TwitterUser> mSelectedItems = new ArrayList<TwitterUser>(); private Long mNewestUserId; private Long mRefreshingNewestUserId; private Long mOldestUserId; private Long mRefreshingUserTweetId; private boolean mMoreUsersAvailable = true; /* * */ public App getApp() { return (App) getActivity().getApplication(); } /* * (non-Javadoc) * * @see * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, * android.view.ViewGroup, android.os.Bundle) */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); mContentHandle = TwitterManager.get().getContentHandle( getContentHandleBase(), getScreenName(), getLaneIdentifier(), getApp().getCurrentAccountKey()); if (mContentHandle == null) { // Occurs when coming back after the app was sleeping. Force refresh // of the adapter in this instance to ensure Fragments are created // correctly. showToast("No Content Handle found, forcing refresh"); Intent intent = new Intent("" + SystemEvent.FORCE_FRAGMENT_PAGER_ADAPTER_REFRESH); LocalBroadcastManager.getInstance(getActivity()).sendBroadcast( intent); return null; } // // // View resultView = inflater.inflate(R.layout.lane, null); configureLaneWidth(resultView); mViewSwitcher = (ViewSwitcher) resultView .findViewById(R.id.profileSwitcher); mUserFeedListAdapter = new UserFeedListAdapter(inflater); MultipleUserSelectionCallback multipleUserSelectionCallback = new MultipleUserSelectionCallback(); mUserFeedListView = (PullToRefreshListView) resultView .findViewById(R.id.pull_to_refresh_listview); mUserFeedListView.getRefreshableView().setOnItemClickListener( mOnUserFeedItemClickListener); mUserFeedListView.getRefreshableView().setChoiceMode( ListView.CHOICE_MODE_MULTIPLE_MODAL); mUserFeedListView.getRefreshableView().setMultiChoiceModeListener( multipleUserSelectionCallback); mUserFeedListView.getRefreshableView().setOnScrollListener( mUserFeedOnScrollListener); mUserFeedListView.getRefreshableView().setAdapter(mUserFeedListAdapter); mUserFeedListView.setOnRefreshListener(mUserFeedOnRefreshListener); mUserFeedListView .setOnLastItemVisibleListener(mUserFeedOnLastItemVisibleListener); // // // TwitterUsers cachedUsers; if (mContentHandle.getUsersType() == UsersType.RETWEETED_BY) { cachedUsers = TwitterManager.get().getUsers(mContentHandle, TwitterPaging.createGetMostRecent(20)); } else { cachedUsers = TwitterManager.get().getUsers(mContentHandle, null); } if (cachedUsers != null) { setUserFeed(cachedUsers); } else { setUserFeed(null); } if (getUserFeed() == null || getUserFeed().getUserCount() == 0) { updateViewVisibility(false); setInitialDownloadState(InitialDownloadState.WAITING); } else { setInitialDownloadState(InitialDownloadState.DOWNLOADED); updateViewVisibility(true); } return resultView; } private TwitterContentHandleBase getContentHandleBase() { return (TwitterContentHandleBase) getArguments().getSerializable( "handleBase"); } private String getScreenName() { return getArguments().getString("screenName"); } private String getLaneIdentifier() { return getArguments().getString("laneIdentifier"); } /* * */ private void setUserFeed(TwitterUsers users) { mNewestUserId = null; mOldestUserId = null; if (users == null) { _mUsersFeed = null; } else { _mUsersFeed = new TwitterUsers(users); if (users.getUserCount() > 0) { mNewestUserId = users.getUser(0).getId(); mOldestUserId = users.getUser(users.getUserCount() - 1).getId(); } } } /* * */ private TwitterUsers getUserFeed() { return _mUsersFeed; } /* * */ private void onRefreshComplete(TwitterUsers feed) { if (feed != null) { setUserFeed(feed); } mUserFeedListAdapter.notifyDataSetChanged(); mUserDataRefreshCallback = null; } /* * */ private void updateViewVisibility(boolean loadHasFinished) { mViewSwitcher.reset(); if (!loadHasFinished && (getUserFeed() == null || getUserFeed().getUserCount() == 0)) { mViewSwitcher.setDisplayedChild(0); } else { mViewSwitcher.setDisplayedChild(1); mUserFeedListAdapter.notifyDataSetChanged(); } } /* * (non-Javadoc) * * @see com.tweetlanes.android.core.view.BaseLaneFragment#UpdateTweetCache() */ @Override public void UpdateTweetCache(TwitterStatus status, boolean deleteStatus) { } /* * (non-Javadoc) * * @see com.tweetlanes.android.core.view.BaseLaneFragment#onJumpToTop() */ @Override public void onJumpToTop() { if (mUserFeedListView != null) { ListView listView = mUserFeedListView.getRefreshableView(); if (listView != null && listView.getAdapter() != null && !listView.getAdapter().isEmpty()) { listView.setSelection(0); } } } /* * (non-Javadoc) * * @see com.tweetlanes.android.core.view.BaseLaneFragment#clearLocalCache() */ @Override public void clearLocalCache() { _mUsersFeed = null; } /* * (non-Javadoc) * * @see com.tweetlanes.android.core.view.BaseLaneFragment#getContentToCache() */ @Override public String getDataToCache() { return null; } /* * */ private final OnScrollListener mUserFeedOnScrollListener = new OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mUserFeedListView.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == 0) { // Restore scrollbar fading, which may have been set to false // when scrolling through items via the volume keys view.setScrollbarFadingEnabled(true); } mUserFeedListView.onScrollStateChanged(view, scrollState); } }; /* * */ private final OnLastItemVisibleListener mUserFeedOnLastItemVisibleListener = new OnLastItemVisibleListener() { @Override public void onLastItemVisible() { mUserDataRefreshCallback = TwitterManager.get() .getFetchUsersInstance().new FinishedCallback() { @Override public void finished(TwitterFetchResult result, TwitterUsers feed) { onRefreshComplete(feed); if (mRefreshingUserTweetId.longValue() == mOldestUserId .longValue()) { mMoreUsersAvailable = false; } mRefreshingUserTweetId = null; } }; if (mOldestUserId != null) { if (mRefreshingUserTweetId == null) { // TODO: Bit of a hack, but does remove an unnecessary fetch // that is triggered here when initializing int count = mUserFeedListAdapter.getCount(); if (count > 2) { mRefreshingUserTweetId = mOldestUserId; TwitterManager.get().getUsers(mContentHandle, TwitterPaging.createGetOlder(mOldestUserId), mUserDataRefreshCallback); } } } } }; /* * */ private final OnRefreshListener mUserFeedOnRefreshListener = new OnRefreshListener() { @Override public void onRefresh() { mUserDataRefreshCallback = TwitterManager.get() .getFetchUsersInstance().new FinishedCallback() { @Override public void finished(TwitterFetchResult result, TwitterUsers feed) { onRefreshComplete(feed); mUserFeedListView.onRefreshComplete(); mRefreshingNewestUserId = null; } }; if (mNewestUserId != null) { if (mRefreshingNewestUserId == null) { mRefreshingNewestUserId = mNewestUserId; TwitterUsers users = TwitterManager.get().getUsers( mContentHandle, TwitterPaging.createGetNewer(mNewestUserId), mUserDataRefreshCallback); if (users == null) { getBaseLaneActivity().finishCurrentActionMode(); } } } else { TwitterManager.get().getUsers(mContentHandle, null, mUserDataRefreshCallback); getBaseLaneActivity().finishCurrentActionMode(); } } }; /* * */ private final OnItemClickListener mOnUserFeedItemClickListener = new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Ignore the PTR item position -= 1; if (position < getUserFeed().getUserCount()) { TwitterUser user = getUserFeed().getUser(position); Intent profileIntent = new Intent(getActivity(), ProfileActivity.class); profileIntent.putExtra("userId", Long.valueOf(user.getId()) .toString()); profileIntent.putExtra("userScreenName", user.getScreenName()); getActivity().startActivityForResult(profileIntent, Constant.REQUEST_CODE_PROFILE); } } }; /* * */ private class MultipleUserSelectionCallback implements ListView.MultiChoiceModeListener { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mode.getMenu().clear(); MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.users_selected, mode.getMenu()); mode.setTitle("Select Users"); configure(mode); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return true; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { final int itemId = item.getItemId(); if (itemId == R.id.action_retweet || itemId == R.id.action_favorite || itemId == R.id.action_manage_friendship) { showToast(getString(R.string.functionality_not_implemented)); mode.finish(); } else if (itemId == R.id.action_report_for_spam || itemId == R.id.action_block) { AccountDescriptor account = getApp().getCurrentAccount(); if (account != null) { TwitterUsers selected = getSelectedUsers(); if (selected != null && selected.getUserCount() > 0) { ArrayList<Long> userIds = new ArrayList<Long>(); for (int i = 0; i < selected.getUserCount(); i++) { userIds.add(selected.getUser(0).getId()); } FinishedCallback callback = TwitterManager .get().getFetchUsersInstance().new FinishedCallback() { @Override public void finished(TwitterFetchResult result, TwitterUsers users) { getBaseLaneActivity().finishCurrentActionMode(); if (getUserFeed() != null) { getUserFeed().remove(users); } onRefreshComplete(getUserFeed()); mUserFeedListView.onRefreshComplete(); updateViewVisibility(true); if (result.isSuccessful() && users != null && users.getUserCount() > 0) { int userCount = users.getUserCount(); String notice; if (itemId == R.id.action_report_for_spam) { if (userCount == 1) { notice = "Reported @" + users.getUser(0) .getScreenName() + " for Spam."; } else { notice = "Reported " + userCount + " users for Spam."; } } else { if (userCount == 1) { notice = "Blocked @" + users.getUser(0) .getScreenName() + "."; } else { notice = "Blocked " + userCount + " users."; } } if (notice != null) { showToast(notice); } } } }; if (item.getItemId() == R.id.action_report_for_spam) { TwitterManager.get().reportSpam(account.getId(), userIds, callback); } else { TwitterManager.get().createBlock(account.getId(), userIds, callback); } } } } else { } return true; } public void onDestroyActionMode(ActionMode mode) { mSelectedItems.clear(); if (!getBaseLaneActivity().composeHasFocus()) { getBaseLaneActivity().setComposeDefault(); } } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { onUserFeedItemViewClick(position, checked); configure(mode); } void configure(ActionMode mode) { BaseLaneActivity baseLaneActivity = (BaseLaneActivity) getActivity(); if (baseLaneActivity == null || baseLaneActivity.isComposing() || mUserFeedListView == null || mUserFeedListView.getRefreshableView() == null || mode == null) { return; } final int checkedCount = mUserFeedListView.getRefreshableView() .getCheckedItemCount(); switch (checkedCount) { case 0: mode.setSubtitle(null); break; default: mode.setSubtitle("" + checkedCount + " selected"); break; } } } /* * */ private void onUserFeedItemViewClick(int position, boolean checked) { position -= 1; // remove the pull-to-refresh item TwitterUser user = getUserFeed().getUser(position); for (int index = 0; index < mSelectedItems.size(); index++) { TwitterUser selectedUser = mSelectedItems.get(index); if (selectedUser != null && user != null) { if (selectedUser.getId() == user.getId()) { mSelectedItems.remove(index); break; } } } if (checked) { mSelectedItems.add(user); } if (mSelectedItems.size() > 0) { getBaseLaneActivity() .setComposeTweetDefault( new ComposeTweetDefault(getApp() .getCurrentAccountScreenName(), getSelectedUsers())); } else { getBaseLaneActivity().setComposeDefault(); } } /* * */ private TwitterUsers getSelectedUsers() { TwitterUsers selectedList = new TwitterUsers(); for (int i = 0; i < mSelectedItems.size(); i++) { TwitterUser selectedUser = mSelectedItems.get(i); if (selectedUser != null) { selectedList.add(selectedUser); } } return selectedList.getUserCount() > 0 ? selectedList : null; } /* * */ /* * private TwitterUser getFirstSelectedUser() { for (int i = 0; i < * mSelectedItems.size(); i++) { UserFeedItemView userFeedItemView = * mSelectedItems.get(i); TwitterUser user = * userFeedItemView.getTwitterUser(); if (user != null) { return user; } } * return null; } */ /* * */ public interface UserFeedItemViewCallbacks { public Activity getActivity(); public LazyImageLoader getProfileImageLoader(); } /* * */ private class UserFeedListAdapter extends BaseAdapter { public UserFeedListAdapter(LayoutInflater inflater) { mInflater = inflater; } /** * The number of items in the list is determined by the number of * speeches in our array. * * @see android.widget.ListAdapter#getCount() */ public int getCount() { int count; if (getUserFeed() != null) { count = getUserFeed().getUserCount() + 1; // +1 for the trailing // LoadMore view } else { count = 1; } return count; } /** * Since the data comes from an array, just returning the index is * sufficent to get at the data. If we were using a more complex data * structure, we would return whatever object represents one row in the * list. * * @see android.widget.ListAdapter#getItem(int) */ public Object getItem(int position) { return position; } /** * Use the array index as a unique id. * * @see android.widget.ListAdapter#getItemId(int) */ public long getItemId(int position) { return position; } /** * Make a TweetFeedItemView to hold each row. */ public View getView(int position, View convertView, ViewGroup parent) { int userCount = getUserFeed() != null ? getUserFeed() .getUserCount() : 0; View resultView; if (userCount == 0 && position == getCount() - 1) { resultView = getLoadMoreView(); } else if (position == userCount) { resultView = getLoadMoreView(); } else { resultView = getUserFeedItemView(position); } return resultView; } /* * */ View getUserFeedItemView(int position) { View convertView = mInflater.inflate(R.layout.user_feed_item, null); TwitterUser user = getUserFeed().getUser(position); UserFeedItemView userFeedItemView = (UserFeedItemView) convertView .findViewById(R.id.userFeedItem); UserFeedItemViewCallbacks callbacks = new UserFeedItemViewCallbacks() { @Override public Activity getActivity() { return UserFeedFragment.this.getActivity(); } @Override public LazyImageLoader getProfileImageLoader() { return getApp().getProfileImageLoader(); } }; userFeedItemView.configure(user, position + 1, callbacks); return userFeedItemView; } /* * */ View getLoadMoreView() { View convertView = mInflater.inflate(R.layout.load_more, null); LoadMoreView loadMoreView = (LoadMoreView) convertView .findViewById(R.id.loadMoreView); LoadMoreView.Mode mode; if (getUserFeed() == null || getUserFeed().getUserCount() == 0) { mode = LoadMoreView.Mode.NONE_FOUND; } else { mode = mMoreUsersAvailable ? LoadMoreView.Mode.LOADING : LoadMoreView.Mode.NO_MORE; } loadMoreView.configure(mode); return loadMoreView; } /** * Remember our context so we can use it when constructing views. */ // private Context mContext; private final LayoutInflater mInflater; } /* * (non-Javadoc) * * @see * com.tweetlanes.android.core.view.BaseLaneFragment#triggerInitialDownload() */ @Override public void triggerInitialDownload() { mUserDataRefreshCallback = TwitterManager.get().getFetchUsersInstance().new FinishedCallback() { @Override public void finished(TwitterFetchResult fetchResult, TwitterUsers users) { onRefreshComplete(users); if (mUserFeedListView != null) { mUserFeedListView.onRefreshComplete(); } updateViewVisibility(true); mUserDataRefreshCallback = null; setInitialDownloadState(InitialDownloadState.DOWNLOADED); } }; TwitterUsers cachedUsers; if (mContentHandle.getUsersType() == UsersType.RETWEETED_BY) { cachedUsers = TwitterManager.get().getUsers(mContentHandle, TwitterPaging.createGetMostRecent(20), mUserDataRefreshCallback); } else { cachedUsers = TwitterManager.get().getUsers(mContentHandle, null, mUserDataRefreshCallback); } if (cachedUsers != null) { if (mUserDataRefreshCallback != null) { mUserDataRefreshCallback.finished(new TwitterFetchResult(true, null), cachedUsers); } } else { setInitialDownloadState(InitialDownloadState.DOWNLOADING); } } }