| package org.wordpress.android.ui.stats; |
| |
| import android.app.Activity; |
| import android.content.res.Resources; |
| import android.os.Bundle; |
| import android.text.TextUtils; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.ArrayAdapter; |
| import android.widget.LinearLayout; |
| |
| import org.wordpress.android.R; |
| import org.wordpress.android.WordPress; |
| import org.wordpress.android.ui.reader.ReaderActivityLauncher; |
| import org.wordpress.android.ui.stats.models.FollowDataModel; |
| import org.wordpress.android.ui.stats.models.FollowerModel; |
| import org.wordpress.android.ui.stats.models.FollowersModel; |
| import org.wordpress.android.ui.stats.service.StatsService; |
| import org.wordpress.android.util.DisplayUtils; |
| import org.wordpress.android.util.FormatUtils; |
| import org.wordpress.android.util.GravatarUtils; |
| import org.wordpress.android.util.UrlUtils; |
| import org.wordpress.android.widgets.WPNetworkImageView; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ThreadPoolExecutor; |
| |
| |
| public class StatsFollowersFragment extends StatsAbstractListFragment { |
| public static final String TAG = StatsFollowersFragment.class.getSimpleName(); |
| |
| private static final String ARG_REST_RESPONSE_FOLLOWERS_EMAIL = "ARG_REST_RESPONSE_FOLLOWERS_EMAIL"; |
| private final Map<String, Integer> userBlogs = new HashMap<>(); |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
| View view = super.onCreateView(inflater, container, savedInstanceState); |
| |
| Resources res = container.getContext().getResources(); |
| |
| String[] titles = { |
| res.getString(R.string.stats_followers_wpcom_selector), |
| res.getString(R.string.stats_followers_email_selector), |
| }; |
| |
| |
| setupTopModulePager(inflater, container, view, titles); |
| |
| mTopPagerContainer.setVisibility(View.VISIBLE); |
| mTotalsLabel.setVisibility(View.VISIBLE); |
| mTotalsLabel.setText(""); |
| |
| return view; |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Single background thread used to create the blogs list in BG |
| ThreadPoolExecutor blogsListCreatorExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); |
| blogsListCreatorExecutor.submit(new Thread() { |
| @Override |
| public void run() { |
| // Read all the dotcomBlog blogs and get the list of home URLs. |
| // This will be used later to check if the user is a member of followers blog marked as private. |
| List<Map<String, Object>> dotComUserBlogs = WordPress.wpDB.getBlogsBy("dotcomFlag=1", |
| new String[]{"homeURL"}); |
| for (Map<String, Object> blog : dotComUserBlogs) { |
| if (blog != null && blog.get("homeURL") != null && blog.get("blogId") != null) { |
| String normURL = normalizeAndRemoveScheme(blog.get("homeURL").toString()); |
| Integer blogID = (Integer) blog.get("blogId"); |
| userBlogs.put(normURL, blogID); |
| } |
| } |
| } |
| }); |
| } |
| |
| private FollowersModel mFollowersWPCOM; |
| private FollowersModel mFollowersEmail; |
| |
| @Override |
| protected boolean hasDataAvailable() { |
| return mFollowersWPCOM != null || mFollowersEmail != null; |
| } |
| @Override |
| protected void saveStatsData(Bundle outState) { |
| if (mFollowersWPCOM != null) { |
| outState.putSerializable(ARG_REST_RESPONSE, mFollowersWPCOM); |
| } |
| if (mFollowersEmail != null) { |
| outState.putSerializable(ARG_REST_RESPONSE_FOLLOWERS_EMAIL, mFollowersEmail); |
| } |
| } |
| @Override |
| protected void restoreStatsData(Bundle savedInstanceState) { |
| if (savedInstanceState.containsKey(ARG_REST_RESPONSE)) { |
| mFollowersWPCOM = (FollowersModel) savedInstanceState.getSerializable(ARG_REST_RESPONSE); |
| } |
| if (savedInstanceState.containsKey(ARG_REST_RESPONSE_FOLLOWERS_EMAIL)) { |
| mFollowersEmail = (FollowersModel) savedInstanceState.getSerializable(ARG_REST_RESPONSE_FOLLOWERS_EMAIL); |
| } |
| } |
| |
| @SuppressWarnings("unused") |
| public void onEventMainThread(StatsEvents.FollowersWPCOMUdated event) { |
| if (!shouldUpdateFragmentOnUpdateEvent(event)) { |
| return; |
| } |
| |
| mFollowersWPCOM = event.mFollowers; |
| updateUI(); |
| } |
| |
| @SuppressWarnings("unused") |
| public void onEventMainThread(StatsEvents.FollowersEmailUdated event) { |
| if (!shouldUpdateFragmentOnUpdateEvent(event)) { |
| return; |
| } |
| |
| mFollowersEmail = event.mFollowers; |
| updateUI(); |
| } |
| |
| @SuppressWarnings("unused") |
| public void onEventMainThread(StatsEvents.SectionUpdateError event) { |
| if (!shouldUpdateFragmentOnErrorEvent(event)) { |
| return; |
| } |
| |
| mFollowersWPCOM = null; |
| mFollowersEmail = null; |
| showErrorUI(event.mError); |
| } |
| |
| @Override |
| protected void updateUI() { |
| if (!isAdded()) { |
| return; |
| } |
| |
| if (!hasDataAvailable()) { |
| showHideNoResultsUI(true); |
| mTotalsLabel.setText(getTotalFollowersLabel(0)); |
| return; |
| } |
| |
| mTotalsLabel.setVisibility(View.VISIBLE); |
| |
| final FollowersModel followersModel = getCurrentDataModel(); |
| |
| if (followersModel != null && followersModel.getFollowers() != null && |
| followersModel.getFollowers().size() > 0) { |
| ArrayAdapter adapter = new DotComFollowerAdapter(getActivity(), followersModel.getFollowers()); |
| StatsUIHelper.reloadLinearLayout(getActivity(), adapter, mList, getMaxNumberOfItemsToShowInList()); |
| showHideNoResultsUI(false); |
| |
| if (mTopPagerSelectedButtonIndex == 0) { |
| mTotalsLabel.setText(getTotalFollowersLabel(followersModel.getTotalWPCom())); |
| } else { |
| mTotalsLabel.setText(getTotalFollowersLabel(followersModel.getTotalEmail())); |
| } |
| |
| if (isSingleView()) { |
| if (followersModel.getPages() > 1) { |
| mBottomPaginationContainer.setVisibility(View.VISIBLE); |
| mTopPaginationContainer.setVisibility(View.VISIBLE); |
| String paginationLabel = String.format( |
| getString(R.string.stats_pagination_label), |
| FormatUtils.formatDecimal(followersModel.getPage()), |
| FormatUtils.formatDecimal(followersModel.getPages()) |
| ); |
| mBottomPaginationText.setText(paginationLabel); |
| mTopPaginationText.setText(paginationLabel); |
| setNavigationButtonsEnabled(true); |
| |
| // Setting up back buttons |
| if (followersModel.getPage() == 1) { |
| // first page. No go back buttons |
| setNavigationBackButtonsVisibility(false); |
| } else { |
| setNavigationBackButtonsVisibility(true); |
| View.OnClickListener clickListener = new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| setNavigationButtonsEnabled(false); |
| refreshStats( |
| followersModel.getPage() - 1, |
| new StatsService.StatsEndpointsEnum[]{sectionsToUpdate()[mTopPagerSelectedButtonIndex]} |
| ); |
| } |
| }; |
| mBottomPaginationGoBackButton.setOnClickListener(clickListener); |
| mTopPaginationGoBackButton.setOnClickListener(clickListener); |
| } |
| |
| // Setting up forward buttons |
| if (followersModel.getPage() == followersModel.getPages()) { |
| // last page. No go forward buttons |
| setNavigationForwardButtonsVisibility(false); |
| } else { |
| setNavigationForwardButtonsVisibility(true); |
| View.OnClickListener clickListener = new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| setNavigationButtonsEnabled(false); |
| refreshStats( |
| followersModel.getPage() + 1, |
| new StatsService.StatsEndpointsEnum[]{sectionsToUpdate()[mTopPagerSelectedButtonIndex]} |
| ); |
| } |
| }; |
| mBottomPaginationGoForwardButton.setOnClickListener(clickListener); |
| mTopPaginationGoForwardButton.setOnClickListener(clickListener); |
| } |
| |
| // Change the total number of followers label by adding the current paging info |
| int startIndex = followersModel.getPage() * StatsService.MAX_RESULTS_REQUESTED_PER_PAGE - StatsService.MAX_RESULTS_REQUESTED_PER_PAGE + 1; |
| int endIndex = startIndex + followersModel.getFollowers().size() - 1; |
| String pagedLabel = getString( |
| mTopPagerSelectedButtonIndex == 0 ? R.string.stats_followers_total_wpcom_paged : R.string.stats_followers_total_email_paged, |
| startIndex, |
| endIndex, |
| FormatUtils.formatDecimal(mTopPagerSelectedButtonIndex == 0 ? followersModel.getTotalWPCom() : followersModel.getTotalEmail()) |
| ); |
| mTotalsLabel.setText(pagedLabel); |
| } else { |
| // No paging required. Hide the controls. |
| mBottomPaginationContainer.setVisibility(View.GONE); |
| mTopPaginationContainer.setVisibility(View.GONE); |
| } |
| } |
| } else { |
| showHideNoResultsUI(true); |
| mBottomPaginationContainer.setVisibility(View.GONE); |
| mTotalsLabel.setText(getTotalFollowersLabel(0)); |
| } |
| |
| // Always visible. Even if the current tab is empty, otherwise the user can't switch tab |
| mTopPagerContainer.setVisibility(View.VISIBLE); |
| } |
| |
| private FollowersModel getCurrentDataModel() { |
| return mTopPagerSelectedButtonIndex == 0 ? mFollowersWPCOM : mFollowersEmail; |
| } |
| |
| private void setNavigationBackButtonsVisibility(boolean visible) { |
| mBottomPaginationGoBackButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| mTopPaginationGoBackButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| } |
| |
| private void setNavigationForwardButtonsVisibility(boolean visible) { |
| mBottomPaginationGoForwardButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| mTopPaginationGoForwardButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); |
| } |
| |
| private void setNavigationButtonsEnabled(boolean enable) { |
| mBottomPaginationGoBackButton.setEnabled(enable); |
| mBottomPaginationGoForwardButton.setEnabled(enable); |
| mTopPaginationGoBackButton.setEnabled(enable); |
| mTopPaginationGoForwardButton.setEnabled(enable); |
| } |
| |
| @Override |
| protected boolean isViewAllOptionAvailable() { |
| if (!hasDataAvailable()) { |
| return false; |
| } |
| FollowersModel followersModel = getCurrentDataModel(); |
| return !(followersModel == null || followersModel.getFollowers() == null |
| || followersModel.getFollowers().size() < MAX_NUM_OF_ITEMS_DISPLAYED_IN_LIST); |
| |
| } |
| |
| private String getTotalFollowersLabel(int total) { |
| final String totalFollowersLabel; |
| |
| if (mTopPagerSelectedButtonIndex == 0) { |
| totalFollowersLabel = getString(R.string.stats_followers_total_wpcom); |
| } else { |
| totalFollowersLabel = getString(R.string.stats_followers_total_email); |
| } |
| |
| return String.format(totalFollowersLabel, FormatUtils.formatDecimal(total)); |
| } |
| |
| @Override |
| protected boolean isExpandableList() { |
| return false; |
| } |
| |
| private class DotComFollowerAdapter extends ArrayAdapter<FollowerModel> { |
| |
| private final List<FollowerModel> list; |
| private final Activity context; |
| private final LayoutInflater inflater; |
| |
| public DotComFollowerAdapter(Activity context, List<FollowerModel> list) { |
| super(context, R.layout.stats_list_cell, list); |
| this.context = context; |
| this.list = list; |
| inflater = LayoutInflater.from(context); |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| View rowView = convertView; |
| // reuse views |
| if (rowView == null) { |
| rowView = inflater.inflate(R.layout.stats_list_cell, parent, false); |
| // set a min-width value that is large enough to contains the "since" string |
| LinearLayout totalContainer = (LinearLayout) rowView.findViewById(R.id.stats_list_cell_total_container); |
| int dp64 = DisplayUtils.dpToPx(rowView.getContext(), 64); |
| totalContainer.setMinimumWidth(dp64); |
| // configure view holder |
| StatsViewHolder viewHolder = new StatsViewHolder(rowView); |
| rowView.setTag(viewHolder); |
| } |
| |
| final FollowerModel currentRowData = list.get(position); |
| final StatsViewHolder holder = (StatsViewHolder) rowView.getTag(); |
| |
| holder.entryTextView.setTextColor(context.getResources().getColor(R.color.stats_text_color)); |
| holder.rowContent.setClickable(false); |
| |
| final FollowDataModel followData = currentRowData.getFollowData(); |
| |
| // entries |
| if (mTopPagerSelectedButtonIndex == 0 && !(TextUtils.isEmpty(currentRowData.getURL()) && followData == null)) { |
| // WPCOM followers with no empty URL or empty follow data |
| |
| final int blogID; |
| if (followData == null) { |
| // If follow data is empty, we cannot follow the blog, or access it in the reader. |
| // We need to check if the user is a member of this blog. |
| // If so, we can launch open the reader, otherwise open the blog in the in-app browser. |
| String normURL = normalizeAndRemoveScheme(currentRowData.getURL()); |
| blogID = userBlogs.containsKey(normURL) ? userBlogs.get(normURL) : Integer.MIN_VALUE; |
| } else { |
| blogID = followData.getSiteID(); |
| } |
| |
| if (blogID > Integer.MIN_VALUE) { |
| // Open the Reader |
| holder.entryTextView.setText(currentRowData.getLabel()); |
| holder.rowContent.setOnClickListener( |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| ReaderActivityLauncher.showReaderBlogPreview( |
| context, |
| blogID |
| ); |
| } |
| }); |
| } else { |
| // Open the in-app web browser |
| holder.setEntryTextOrLink(currentRowData.getURL(), currentRowData.getLabel()); |
| } |
| holder.entryTextView.setTextColor(context.getResources().getColor(R.color.stats_link_text_color)); |
| } else { |
| // Email followers, or wpcom followers with empty URL and no blogID |
| holder.setEntryText(currentRowData.getLabel()); |
| } |
| |
| // since date |
| holder.totalsTextView.setText( |
| StatsUtils.getSinceLabel( |
| context, |
| currentRowData.getDateSubscribed() |
| ) |
| ); |
| |
| // Avatar |
| holder.networkImageView.setImageUrl( |
| GravatarUtils.fixGravatarUrl(currentRowData.getAvatar(), mResourceVars.headerAvatarSizePx), |
| WPNetworkImageView.ImageType.AVATAR); |
| holder.networkImageView.setVisibility(View.VISIBLE); |
| |
| if (followData == null) { |
| holder.imgMore.setVisibility(View.GONE); |
| holder.imgMore.setClickable(false); |
| } else { |
| holder.imgMore.setVisibility(View.VISIBLE); |
| holder.imgMore.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| FollowHelper fh = new FollowHelper(context); |
| fh.showPopup(holder.imgMore, followData); |
| } |
| }); |
| } |
| |
| return rowView; |
| } |
| |
| |
| } |
| |
| private static String normalizeAndRemoveScheme(String url) { |
| if (TextUtils.isEmpty(url)) { |
| return ""; |
| } |
| String normURL = UrlUtils.normalizeUrl(url.toLowerCase()); |
| int pos = normURL.indexOf("://"); |
| if (pos > -1) { |
| return normURL.substring(pos + 3); |
| } else { |
| return normURL; |
| } |
| } |
| |
| @Override |
| protected int getEntryLabelResId() { |
| return R.string.stats_entry_followers; |
| } |
| |
| @Override |
| protected int getTotalsLabelResId() { |
| return R.string.stats_totals_followers; |
| } |
| |
| @Override |
| protected int getEmptyLabelTitleResId() { |
| return R.string.stats_empty_followers; |
| } |
| |
| @Override |
| protected int getEmptyLabelDescResId() { |
| return R.string.stats_empty_followers_desc; |
| } |
| |
| @Override |
| protected StatsService.StatsEndpointsEnum[] sectionsToUpdate() { |
| return new StatsService.StatsEndpointsEnum[]{ |
| StatsService.StatsEndpointsEnum.FOLLOWERS_WPCOM, StatsService.StatsEndpointsEnum.FOLLOWERS_EMAIL |
| }; |
| } |
| |
| @Override |
| public String getTitle() { |
| return getString(R.string.stats_view_followers); |
| } |
| } |