diff options
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/ui/stats/StatsUIHelper.java')
-rw-r--r-- | WordPress/src/main/java/org/wordpress/android/ui/stats/StatsUIHelper.java | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsUIHelper.java b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsUIHelper.java new file mode 100644 index 000000000..441ed3050 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsUIHelper.java @@ -0,0 +1,344 @@ +package org.wordpress.android.ui.stats; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Point; +import android.text.Spannable; +import android.text.style.URLSpan; +import android.util.SparseBooleanArray; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.Interpolator; +import android.view.animation.RotateAnimation; +import android.view.animation.ScaleAnimation; +import android.widget.ExpandableListAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListAdapter; + +import org.wordpress.android.R; +import org.wordpress.android.WordPress; +import org.wordpress.android.util.DisplayUtils; + +class StatsUIHelper { + // Max number of rows to show in a stats fragment + private static final int STATS_GROUP_MAX_ITEMS = 10; + private static final int STATS_CHILD_MAX_ITEMS = 50; + private static final int ANIM_DURATION = 150; + + // Used for tablet UI + private static final int TABLET_720DP = 720; + private static final int TABLET_600DP = 600; + + private static boolean isInLandscape(Activity act) { + Display display = act.getWindowManager().getDefaultDisplay(); + Point point = new Point(); + display.getSize(point); + return (point.y < point.x); + } + + // Load more bars for 720DP tablets + private static boolean shouldLoadMoreBars() { + return (StatsUtils.getSmallestWidthDP() >= TABLET_720DP); + } + + public static void reloadLinearLayout(Context ctx, ListAdapter adapter, LinearLayout linearLayout, int maxNumberOfItemsToshow) { + if (ctx == null || linearLayout == null || adapter == null) { + return; + } + + // limit number of items to show otherwise it would cause performance issues on the LinearLayout + int count = Math.min(adapter.getCount(), maxNumberOfItemsToshow); + + if (count == 0) { + linearLayout.removeAllViews(); + return; + } + + int numExistingViews = linearLayout.getChildCount(); + // remove excess views + if (count < numExistingViews) { + int numToRemove = numExistingViews - count; + linearLayout.removeViews(count, numToRemove); + numExistingViews = count; + } + + int bgColor = Color.TRANSPARENT; + for (int i = 0; i < count; i++) { + final View view; + // reuse existing view when possible + if (i < numExistingViews) { + View convertView = linearLayout.getChildAt(i); + view = adapter.getView(i, convertView, linearLayout); + view.setBackgroundColor(bgColor); + setViewBackgroundWithoutResettingPadding(view, i == 0 ? 0 : R.drawable.stats_list_item_background); + } else { + view = adapter.getView(i, null, linearLayout); + view.setBackgroundColor(bgColor); + setViewBackgroundWithoutResettingPadding(view, i == 0 ? 0 : R.drawable.stats_list_item_background); + linearLayout.addView(view); + } + } + linearLayout.invalidate(); + } + + /** + * + * Padding information are reset when changing the background Drawable on a View. + * The reason why setting an image resets the padding is because 9-patch images can encode padding. + * + * See http://stackoverflow.com/a/10469121 and + * http://www.mail-archive.com/android-developers@googlegroups.com/msg09595.html + * + * @param v The view to apply the background resource + * @param backgroundResId The resource ID + */ + private static void setViewBackgroundWithoutResettingPadding(final View v, final int backgroundResId) { + final int paddingBottom = v.getPaddingBottom(), paddingLeft = v.getPaddingLeft(); + final int paddingRight = v.getPaddingRight(), paddingTop = v.getPaddingTop(); + v.setBackgroundResource(backgroundResId); + v.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } + + public static void reloadLinearLayout(Context ctx, ListAdapter adapter, LinearLayout linearLayout) { + reloadLinearLayout(ctx, adapter, linearLayout, STATS_GROUP_MAX_ITEMS); + } + + public static void reloadGroupViews(final Context ctx, + final ExpandableListAdapter mAdapter, + final SparseBooleanArray mGroupIdToExpandedMap, + final LinearLayout mLinearLayout) { + reloadGroupViews(ctx, mAdapter, mGroupIdToExpandedMap, mLinearLayout, STATS_GROUP_MAX_ITEMS); + } + + public static void reloadGroupViews(final Context ctx, + final ExpandableListAdapter mAdapter, + final SparseBooleanArray mGroupIdToExpandedMap, + final LinearLayout mLinearLayout, + final int maxNumberOfItemsToshow) { + if (ctx == null || mLinearLayout == null || mAdapter == null || mGroupIdToExpandedMap == null) { + return; + } + + int groupCount = Math.min(mAdapter.getGroupCount(), maxNumberOfItemsToshow); + if (groupCount == 0) { + mLinearLayout.removeAllViews(); + return; + } + + int numExistingGroupViews = mLinearLayout.getChildCount(); + + // remove excess views + if (groupCount < numExistingGroupViews) { + int numToRemove = numExistingGroupViews - groupCount; + mLinearLayout.removeViews(groupCount, numToRemove); + numExistingGroupViews = groupCount; + } + + int bgColor = Color.TRANSPARENT; + + // add each group + for (int i = 0; i < groupCount; i++) { + boolean isExpanded = mGroupIdToExpandedMap.get(i); + + // reuse existing view when possible + final View groupView; + if (i < numExistingGroupViews) { + View convertView = mLinearLayout.getChildAt(i); + groupView = mAdapter.getGroupView(i, isExpanded, convertView, mLinearLayout); + groupView.setBackgroundColor(bgColor); + setViewBackgroundWithoutResettingPadding(groupView, i == 0 ? 0 : R.drawable.stats_list_item_background); + } else { + groupView = mAdapter.getGroupView(i, isExpanded, null, mLinearLayout); + groupView.setBackgroundColor(bgColor); + setViewBackgroundWithoutResettingPadding(groupView, i == 0 ? 0 : R.drawable.stats_list_item_background); + mLinearLayout.addView(groupView); + } + + // groupView is recycled, we need to reset it to the original state. + ViewGroup childContainer = (ViewGroup) groupView.findViewById(R.id.layout_child_container); + if (childContainer != null) { + childContainer.setVisibility(View.GONE); + } + // Remove any other prev animations set on the chevron + final ImageView chevron = (ImageView) groupView.findViewById(R.id.stats_list_cell_chevron); + if (chevron != null) { + chevron.clearAnimation(); + chevron.setImageResource(R.drawable.stats_chevron_right); + } + + // add children if this group is expanded + if (isExpanded) { + StatsUIHelper.showChildViews(mAdapter, mLinearLayout, i, groupView, false); + } + + // toggle expand/collapse when group view is tapped + final int groupPosition = i; + groupView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mAdapter.getChildrenCount(groupPosition) == 0) { + return; + } + boolean shouldExpand = !mGroupIdToExpandedMap.get(groupPosition); + mGroupIdToExpandedMap.put(groupPosition, shouldExpand); + if (shouldExpand) { + StatsUIHelper.showChildViews(mAdapter, mLinearLayout, groupPosition, groupView, true); + } else { + StatsUIHelper.hideChildViews(groupView, groupPosition, true); + } + } + }); + } + } + + /* + * interpolator for all expand/collapse animations + */ + private static Interpolator getInterpolator() { + return new AccelerateInterpolator(); + } + + private static void hideChildViews(View groupView, int groupPosition, boolean animate) { + final ViewGroup childContainer = (ViewGroup) groupView.findViewById(R.id.layout_child_container); + if (childContainer == null) { + return; + } + + if (childContainer.getVisibility() != View.GONE) { + if (animate) { + Animation expand = new ScaleAnimation(1.0f, 1.0f, 1.0f, 0.0f); + expand.setDuration(ANIM_DURATION); + expand.setInterpolator(getInterpolator()); + expand.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { } + @Override + public void onAnimationEnd(Animation animation) { + childContainer.setVisibility(View.GONE); + } + @Override + public void onAnimationRepeat(Animation animation) { } + }); + childContainer.startAnimation(expand); + } else { + childContainer.setVisibility(View.GONE); + } + } + StatsUIHelper.setGroupChevron(false, groupView, groupPosition, animate); + } + + /* + * shows the correct up/down chevron for the passed group + */ + private static void setGroupChevron(final boolean isGroupExpanded, View groupView, int groupPosition, boolean animate) { + final ImageView chevron = (ImageView) groupView.findViewById(R.id.stats_list_cell_chevron); + if (chevron == null) { + return; + } + if (isGroupExpanded) { + // change the background of the parent + setViewBackgroundWithoutResettingPadding(groupView, R.drawable.stats_list_item_expanded_background); + } else { + setViewBackgroundWithoutResettingPadding(groupView, groupPosition == 0 ? 0 : R.drawable.stats_list_item_background); + } + + chevron.clearAnimation(); // Remove any other prev animations set on the chevron + if (animate) { + // make sure we start with the correct chevron for the prior state before animating it + chevron.setImageResource(isGroupExpanded ? R.drawable.stats_chevron_right : R.drawable.stats_chevron_down); + float start = (isGroupExpanded ? 0.0f : 0.0f); + float end = (isGroupExpanded ? 90.0f : -90.0f); + Animation rotate = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f); + rotate.setDuration(ANIM_DURATION); + rotate.setInterpolator(getInterpolator()); + rotate.setFillAfter(true); + chevron.startAnimation(rotate); + } else { + chevron.setImageResource(isGroupExpanded ? R.drawable.stats_chevron_down : R.drawable.stats_chevron_right); + } + } + + private static void showChildViews(ExpandableListAdapter mAdapter, LinearLayout mLinearLayout, + int groupPosition, View groupView, boolean animate) { + int childCount = Math.min(mAdapter.getChildrenCount(groupPosition), STATS_CHILD_MAX_ITEMS); + if (childCount == 0) { + return; + } + + final ViewGroup childContainer = (ViewGroup) groupView.findViewById(R.id.layout_child_container); + if (childContainer == null) { + return; + } + + int numExistingViews = childContainer.getChildCount(); + if (childCount < numExistingViews) { + int numToRemove = numExistingViews - childCount; + childContainer.removeViews(childCount, numToRemove); + numExistingViews = childCount; + } + + for (int i = 0; i < childCount; i++) { + boolean isLastChild = (i == childCount - 1); + if (i < numExistingViews) { + View convertView = childContainer.getChildAt(i); + mAdapter.getChildView(groupPosition, i, isLastChild, convertView, mLinearLayout); + } else { + View childView = mAdapter.getChildView(groupPosition, i, isLastChild, null, mLinearLayout); + // remove the right/left padding so the child total aligns to left + childView.setPadding(0, + childView.getPaddingTop(), + 0, + isLastChild ? 0 : childView.getPaddingBottom()); // No padding bottom on last child + setViewBackgroundWithoutResettingPadding(childView, R.drawable.stats_list_item_child_background); + childContainer.addView(childView); + } + } + + if (childContainer.getVisibility() != View.VISIBLE) { + if (animate) { + Animation expand = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f); + expand.setDuration(ANIM_DURATION); + expand.setInterpolator(getInterpolator()); + childContainer.startAnimation(expand); + } + childContainer.setVisibility(View.VISIBLE); + } + + StatsUIHelper.setGroupChevron(true, groupView, groupPosition, animate); + } + + /** + * Removes URL underlines in a string by replacing URLSpan occurrences by + * URLSpanNoUnderline objects. + * + * @param pText A Spannable object. For example, a TextView casted as + * Spannable. + */ + public static void removeUnderlines(Spannable pText) { + URLSpan[] spans = pText.getSpans(0, pText.length(), URLSpan.class); + + for(URLSpan span:spans) { + int start = pText.getSpanStart(span); + int end = pText.getSpanEnd(span); + pText.removeSpan(span); + span = new URLSpanNoUnderline(span.getURL()); + pText.setSpan(span, start, end, 0); + } + } + + public static int getNumOfBarsToShow() { + if (StatsUtils.getSmallestWidthDP() >= TABLET_720DP && DisplayUtils.isLandscape(WordPress.getContext())) { + return 15; + } else if (StatsUtils.getSmallestWidthDP() >= TABLET_600DP) { + return 10; + } else { + return 7; + } + } +} |