package org.wordpress.android.ui.main; import android.content.Context; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.models.AccountHelper; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.BlogUtils; import org.wordpress.android.util.GravatarUtils; import org.wordpress.android.util.MapUtils; import org.wordpress.android.util.StringUtils; import org.wordpress.android.widgets.WPNetworkImageView; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; class SitePickerAdapter extends RecyclerView.Adapter { interface OnSiteClickListener { void onSiteClick(SiteRecord site); } interface OnSelectedCountChangedListener { void onSelectedCountChanged(int numSelected); } private final int mTextColorNormal; private final int mTextColorHidden; private static int mBlavatarSz; private SiteList mSites = new SiteList(); private final int mCurrentLocalId; private final Drawable mSelectedItemBackground; private final LayoutInflater mInflater; private final HashSet mSelectedPositions = new HashSet<>(); private boolean mIsMultiSelectEnabled; private final boolean mIsInSearchMode; private boolean mShowHiddenSites = false; private boolean mShowSelfHostedSites = true; private String mLastSearch; private SiteList mAllSites; private OnSiteClickListener mSiteSelectedListener; private OnSelectedCountChangedListener mSelectedCountListener; class SiteViewHolder extends RecyclerView.ViewHolder { private final ViewGroup layoutContainer; private final TextView txtTitle; private final TextView txtDomain; private final WPNetworkImageView imgBlavatar; private final View divider; private Boolean isSiteHidden; public SiteViewHolder(View view) { super(view); layoutContainer = (ViewGroup) view.findViewById(R.id.layout_container); txtTitle = (TextView) view.findViewById(R.id.text_title); txtDomain = (TextView) view.findViewById(R.id.text_domain); imgBlavatar = (WPNetworkImageView) view.findViewById(R.id.image_blavatar); divider = view.findViewById(R.id.divider); isSiteHidden = null; itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int clickedPosition = getAdapterPosition(); if (isValidPosition(clickedPosition)) { if (mIsMultiSelectEnabled) { toggleSelection(clickedPosition); } else if (mSiteSelectedListener != null) { mSiteSelectedListener.onSiteClick(getItem(clickedPosition)); } } else { AppLog.w(AppLog.T.MAIN, "site picker > invalid clicked position " + clickedPosition); } } }); } } public SitePickerAdapter(Context context, int currentLocalBlogId, String lastSearch, boolean isInSearchMode) { super(); setHasStableIds(true); mLastSearch = StringUtils.notNullStr(lastSearch); mAllSites = new SiteList(); mIsInSearchMode = isInSearchMode; mCurrentLocalId = currentLocalBlogId; mInflater = LayoutInflater.from(context); mBlavatarSz = context.getResources().getDimensionPixelSize(R.dimen.blavatar_sz); mTextColorNormal = context.getResources().getColor(R.color.grey_dark); mTextColorHidden = context.getResources().getColor(R.color.grey); mSelectedItemBackground = new ColorDrawable(context.getResources().getColor(R.color.translucent_grey_lighten_20)); loadSites(); } @Override public int getItemCount() { return mSites.size(); } @Override public long getItemId(int position) { return getItem(position).localId; } private SiteRecord getItem(int position) { return mSites.get(position); } void setOnSelectedCountChangedListener(OnSelectedCountChangedListener listener) { mSelectedCountListener = listener; } public void setOnSiteClickListener(OnSiteClickListener listener) { mSiteSelectedListener = listener; } @Override public SiteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = mInflater.inflate(R.layout.site_picker_listitem, parent, false); return new SiteViewHolder(itemView); } @Override public void onBindViewHolder(SiteViewHolder holder, int position) { SiteRecord site = getItem(position); holder.txtTitle.setText(site.getBlogNameOrHomeURL()); holder.txtDomain.setText(site.homeURL); holder.imgBlavatar.setImageUrl(site.blavatarUrl, WPNetworkImageView.ImageType.BLAVATAR); if (site.localId == mCurrentLocalId || (mIsMultiSelectEnabled && isItemSelected(position))) { holder.layoutContainer.setBackgroundDrawable(mSelectedItemBackground); } else { holder.layoutContainer.setBackgroundDrawable(null); } // different styling for visible/hidden sites if (holder.isSiteHidden == null || holder.isSiteHidden != site.isHidden) { holder.isSiteHidden = site.isHidden; holder.txtTitle.setTextColor(site.isHidden ? mTextColorHidden : mTextColorNormal); holder.txtTitle.setTypeface(holder.txtTitle.getTypeface(), site.isHidden ? Typeface.NORMAL : Typeface.BOLD); holder.imgBlavatar.setAlpha(site.isHidden ? 0.5f : 1f); } // hide the divider for the last item boolean isLastItem = (position == getItemCount() - 1); holder.divider.setVisibility(isLastItem ? View.INVISIBLE : View.VISIBLE); } public String getLastSearch() { return mLastSearch; } public void setLastSearch(String lastSearch) { mLastSearch = lastSearch; } public boolean getIsInSearchMode() { return mIsInSearchMode; } public void searchSites(String searchText) { mLastSearch = searchText; mSites = filteredSitesByText(mAllSites); notifyDataSetChanged(); } private boolean isValidPosition(int position) { return (position >= 0 && position < mSites.size()); } /* * called when the user chooses to edit the visibility of wp.com blogs */ void setEnableEditMode(boolean enable) { if (mIsMultiSelectEnabled == enable) return; if (enable) { mShowHiddenSites = true; mShowSelfHostedSites = false; } else { mShowHiddenSites = false; mShowSelfHostedSites = true; } mIsMultiSelectEnabled = enable; mSelectedPositions.clear(); loadSites(); } int getNumSelected() { return mSelectedPositions.size(); } int getNumHiddenSelected() { int numHidden = 0; for (Integer i: mSelectedPositions) { if (mSites.get(i).isHidden) { numHidden++; } } return numHidden; } int getNumVisibleSelected() { int numVisible = 0; for (Integer i: mSelectedPositions) { if (!mSites.get(i).isHidden) { numVisible++; } } return numVisible; } private void toggleSelection(int position) { setItemSelected(position, !isItemSelected(position)); } private boolean isItemSelected(int position) { return mSelectedPositions.contains(position); } private void setItemSelected(int position, boolean isSelected) { if (isItemSelected(position) == isSelected) { return; } if (isSelected) { mSelectedPositions.add(position); } else { mSelectedPositions.remove(position); } notifyItemChanged(position); if (mSelectedCountListener != null) { mSelectedCountListener.onSelectedCountChanged(getNumSelected()); } } void selectAll() { if (mSelectedPositions.size() == mSites.size()) return; mSelectedPositions.clear(); for (int i = 0; i < mSites.size(); i++) { mSelectedPositions.add(i); } notifyDataSetChanged(); if (mSelectedCountListener != null) { mSelectedCountListener.onSelectedCountChanged(getNumSelected()); } } void deselectAll() { if (mSelectedPositions.size() == 0) return; mSelectedPositions.clear(); notifyDataSetChanged(); if (mSelectedCountListener != null) { mSelectedCountListener.onSelectedCountChanged(getNumSelected()); } } private SiteList getSelectedSites() { SiteList sites = new SiteList(); if (!mIsMultiSelectEnabled) { return sites; } for (Integer position : mSelectedPositions) { if (isValidPosition(position)) sites.add(mSites.get(position)); } return sites; } SiteList getHiddenSites() { SiteList hiddenSites = new SiteList(); for (SiteRecord site: mSites) { if (site.isHidden) { hiddenSites.add(site); } } return hiddenSites; } void setVisibilityForSelectedSites(boolean makeVisible) { SiteList sites = getSelectedSites(); if (sites != null && sites.size() > 0) { for (SiteRecord site: sites) { int index = mSites.indexOfSite(site); if (index > -1) { mSites.get(index).isHidden = !makeVisible; } } } } void loadSites() { if (mIsTaskRunning) { AppLog.w(AppLog.T.UTILS, "site picker > already loading sites"); } else { new LoadSitesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } private SiteList filteredSitesByTextIfInSearchMode(SiteList sites) { if (!mIsInSearchMode) { return sites; } else { return filteredSitesByText(sites); } } private SiteList filteredSitesByText(SiteList sites) { SiteList filteredSiteList = new SiteList(); for (int i = 0; i < sites.size(); i++) { SiteRecord record = sites.get(i); String siteNameLowerCase = record.blogName.toLowerCase(); String hostNameLowerCase = record.homeURL.toLowerCase(); if (siteNameLowerCase.contains(mLastSearch.toLowerCase()) || hostNameLowerCase.contains(mLastSearch.toLowerCase())) { filteredSiteList.add(record); } } return filteredSiteList; } /* * AsyncTask which loads sites from database and populates the adapter */ private boolean mIsTaskRunning; private class LoadSitesTask extends AsyncTask { @Override protected void onPreExecute() { super.onPreExecute(); mIsTaskRunning = true; } @Override protected void onCancelled() { super.onCancelled(); mIsTaskRunning = false; } @Override protected Void doInBackground(Void... params) { List> blogs; String[] extraFields = {"isHidden", "dotcomFlag", "homeURL"}; if (mIsInSearchMode) { blogs = WordPress.wpDB.getBlogsBy(null, extraFields); } else { blogs = getBlogsForCurrentView(extraFields); } SiteList sites = new SiteList(blogs); // sort by blog/host final long primaryBlogId = AccountHelper.getDefaultAccount().getPrimaryBlogId(); Collections.sort(sites, new Comparator() { public int compare(SiteRecord site1, SiteRecord site2) { if (primaryBlogId > 0) { if (site1.blogId == primaryBlogId) { return -1; } else if (site2.blogId == primaryBlogId) { return 1; } } return site1.getBlogNameOrHomeURL().compareToIgnoreCase(site2.getBlogNameOrHomeURL()); } }); if (mSites == null || !mSites.isSameList(sites)) { mAllSites = (SiteList) sites.clone(); mSites = filteredSitesByTextIfInSearchMode(sites); } return null; } @Override protected void onPostExecute(Void results) { notifyDataSetChanged(); mIsTaskRunning = false; } private List> getBlogsForCurrentView(String[] extraFields) { if (mShowHiddenSites) { if (mShowSelfHostedSites) { // all self-hosted blogs and all wp.com blogs return WordPress.wpDB.getBlogsBy(null, extraFields); } else { // only wp.com blogs return WordPress.wpDB.getBlogsBy("dotcomFlag=1", extraFields); } } else { if (mShowSelfHostedSites) { // all self-hosted blogs plus visible wp.com blogs return WordPress.wpDB.getBlogsBy("dotcomFlag=0 OR (isHidden=0 AND dotcomFlag=1) ", extraFields); } else { // only visible wp.com blogs return WordPress.wpDB.getBlogsBy("isHidden=0 AND dotcomFlag=1", extraFields); } } } } /** * SiteRecord is a simplified version of the full account (blog) record */ static class SiteRecord { final int localId; final int blogId; final String blogName; final String homeURL; final String url; final String blavatarUrl; final boolean isDotCom; boolean isHidden; SiteRecord(Map account) { localId = MapUtils.getMapInt(account, "id"); blogId = MapUtils.getMapInt(account, "blogId"); blogName = BlogUtils.getBlogNameOrHomeURLFromAccountMap(account); homeURL = BlogUtils.getHomeURLOrHostNameFromAccountMap(account); url = MapUtils.getMapStr(account, "url"); blavatarUrl = GravatarUtils.blavatarFromUrl(url, mBlavatarSz); isDotCom = MapUtils.getMapBool(account, "dotcomFlag"); isHidden = MapUtils.getMapBool(account, "isHidden"); } String getBlogNameOrHomeURL() { if (TextUtils.isEmpty(blogName)) { return homeURL; } return blogName; } } static class SiteList extends ArrayList { SiteList() { } SiteList(List> accounts) { if (accounts != null) { for (Map account : accounts) { add(new SiteRecord(account)); } } } boolean isSameList(SiteList sites) { if (sites == null || sites.size() != this.size()) { return false; } int i; for (SiteRecord site: sites) { i = indexOfSite(site); if (i == -1 || this.get(i).isHidden != site.isHidden) { return false; } } return true; } int indexOfSite(SiteRecord site) { if (site != null && site.blogId > 0) { for (int i = 0; i < size(); i++) { if (site.blogId == this.get(i).blogId) { return i; } } } return -1; } } }