diff options
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/ui/stats/StatsVisitorsAndViewsFragment.java')
-rw-r--r-- | WordPress/src/main/java/org/wordpress/android/ui/stats/StatsVisitorsAndViewsFragment.java | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsVisitorsAndViewsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsVisitorsAndViewsFragment.java new file mode 100644 index 000000000..230064f6f --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsVisitorsAndViewsFragment.java @@ -0,0 +1,846 @@ +package org.wordpress.android.ui.stats; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CheckedTextView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.GraphViewSeries; + +import org.wordpress.android.R; +import org.wordpress.android.WordPress; +import org.wordpress.android.analytics.AnalyticsTracker; +import org.wordpress.android.models.Blog; +import org.wordpress.android.ui.stats.models.VisitModel; +import org.wordpress.android.ui.stats.models.VisitsModel; +import org.wordpress.android.ui.stats.service.StatsService; +import org.wordpress.android.util.AnalyticsUtils; +import org.wordpress.android.util.AppLog; +import org.wordpress.android.util.DisplayUtils; +import org.wordpress.android.util.FormatUtils; +import org.wordpress.android.util.NetworkUtils; +import org.wordpress.android.util.StringUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class StatsVisitorsAndViewsFragment extends StatsAbstractFragment + implements StatsBarGraph.OnGestureListener { + + public static final String TAG = StatsVisitorsAndViewsFragment.class.getSimpleName(); + private static final String ARG_SELECTED_GRAPH_BAR = "ARG_SELECTED_GRAPH_BAR"; + private static final String ARG_PREV_NUMBER_OF_BARS = "ARG_PREV_NUMBER_OF_BARS"; + private static final String ARG_SELECTED_OVERVIEW_ITEM = "ARG_SELECTED_OVERVIEW_ITEM"; + private static final String ARG_CHECKBOX_SELECTED = "ARG_CHECKBOX_SELECTED"; + + + private LinearLayout mGraphContainer; + private LinearLayout mNoActivtyThisPeriodContainer; + private StatsBarGraph mGraphView; + private LinearLayout mModuleButtonsContainer; + private TextView mDateTextView; + private String[] mStatsDate; + + private LinearLayout mLegendContainer; + private CheckedTextView mLegendLabel; + private LinearLayout mVisitorsCheckboxContainer; + private CheckBox mVisitorsCheckbox; + private boolean mIsCheckboxChecked = true; + + private OnDateChangeListener mListener; + private OnOverviewItemChangeListener mOverviewItemChangeListener; + + private final OverviewLabel[] overviewItems = {OverviewLabel.VIEWS, OverviewLabel.VISITORS, OverviewLabel.LIKES, + OverviewLabel.COMMENTS}; + + // Restore the following variables on restart + private VisitsModel mVisitsData; + private int mSelectedOverviewItemIndex = 0; + private int mSelectedBarGraphBarIndex = -1; + private int mPrevNumberOfBarsGraph = -1; + + // Container Activity must implement this interface + public interface OnDateChangeListener { + void onDateChanged(String blogID, StatsTimeframe timeframe, String newDate); + } + + // Container Activity must implement this interface + public interface OnOverviewItemChangeListener { + void onOverviewItemChanged(OverviewLabel newItem); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnDateChangeListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnDateChangeListener"); + } + try { + mOverviewItemChangeListener = (OnOverviewItemChangeListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnOverviewItemChangeListener"); + } + } + + void setSelectedOverviewItem(OverviewLabel itemToSelect) { + for (int i = 0; i < overviewItems.length; i++) { + if (overviewItems[i] == itemToSelect) { + mSelectedOverviewItemIndex = i; + return; + } + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.stats_visitors_and_views_fragment, container, false); + + mDateTextView = (TextView) view.findViewById(R.id.stats_summary_date); + mGraphContainer = (LinearLayout) view.findViewById(R.id.stats_bar_chart_fragment_container); + mModuleButtonsContainer = (LinearLayout) view.findViewById(R.id.stats_pager_tabs); + mNoActivtyThisPeriodContainer = (LinearLayout) view.findViewById(R.id.stats_bar_chart_no_activity); + + mLegendContainer = (LinearLayout) view.findViewById(R.id.stats_legend_container); + mLegendLabel = (CheckedTextView) view.findViewById(R.id.stats_legend_label); + mLegendLabel.setCheckMarkDrawable(null); // Make sure to set a null drawable here. Otherwise the touching area is the same of a TextView + mVisitorsCheckboxContainer = (LinearLayout) view.findViewById(R.id.stats_checkbox_visitors_container); + mVisitorsCheckbox = (CheckBox) view.findViewById(R.id.stats_checkbox_visitors); + mVisitorsCheckbox.setOnClickListener(onCheckboxClicked); + + // Fix an issue on devices with 4.1 or lower, where the Checkbox already uses padding by default internally and overriding it with paddingLeft + // causes the issue report here https://github.com/wordpress-mobile/WordPress-Android/pull/2377#issuecomment-77067993 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + mVisitorsCheckbox.setPadding(getResources().getDimensionPixelSize(R.dimen.margin_medium), 0, 0, 0); + } + + // Make sure we've all the info to build the tab correctly. This is ALWAYS true + if (mModuleButtonsContainer.getChildCount() == overviewItems.length) { + for (int i = 0; i < mModuleButtonsContainer.getChildCount(); i++) { + LinearLayout currentTab = (LinearLayout) mModuleButtonsContainer.getChildAt(i); + boolean isLastItem = i == (overviewItems.length - 1); + boolean isChecked = i == mSelectedOverviewItemIndex; + TabViewHolder currentTabViewHolder = new TabViewHolder(currentTab, overviewItems[i], isChecked, isLastItem); + currentTab.setOnClickListener(TopButtonsOnClickListener); + currentTab.setTag(currentTabViewHolder); + } + mModuleButtonsContainer.setVisibility(View.VISIBLE); + } + + return view; + } + + private class TabViewHolder { + final LinearLayout tab; + final LinearLayout innerContainer; + final TextView label; + final TextView value; + final ImageView icon; + final OverviewLabel labelItem; + boolean isChecked = false; + boolean isLastItem = false; + + public TabViewHolder(LinearLayout currentTab, OverviewLabel labelItem, boolean checked, boolean isLastItem) { + tab = currentTab; + innerContainer = (LinearLayout) currentTab.findViewById(R.id.stats_visitors_and_views_tab_inner_container); + label = (TextView) currentTab.findViewById(R.id.stats_visitors_and_views_tab_label); + label.setText(labelItem.getLabel()); + value = (TextView) currentTab.findViewById(R.id.stats_visitors_and_views_tab_value); + icon = (ImageView) currentTab.findViewById(R.id.stats_visitors_and_views_tab_icon); + this.labelItem = labelItem; + this.isChecked = checked; + this.isLastItem = isLastItem; + updateBackGroundAndIcon(0); + } + + private Drawable getTabIcon() { + switch (labelItem) { + case VISITORS: + return getResources().getDrawable(R.drawable.stats_icon_visitors); + case COMMENTS: + return getResources().getDrawable(R.drawable.stats_icon_comments); + case LIKES: + return getResources().getDrawable(R.drawable.stats_icon_likes); + default: + // Views and when no prev match + return getResources().getDrawable(R.drawable.stats_icon_views); + } + } + + public void updateBackGroundAndIcon(int currentValue) { + if (isChecked) { + value.setTextColor(getResources().getColor(R.color.orange_jazzy)); + } else { + if (currentValue == 0) { + value.setTextColor(getResources().getColor(R.color.grey)); + } else { + value.setTextColor(getResources().getColor(R.color.blue_wordpress)); + } + } + + icon.setImageDrawable(getTabIcon()); + + if (isLastItem) { + if (isChecked) { + tab.setBackgroundResource(R.drawable.stats_visitors_and_views_button_latest_white); + } else { + tab.setBackgroundResource(R.drawable.stats_visitors_and_views_button_latest_blue_light); + } + } else { + if (isChecked) { + tab.setBackgroundResource(R.drawable.stats_visitors_and_views_button_white); + } else { + tab.setBackgroundResource(R.drawable.stats_visitors_and_views_button_blue_light); + } + } + } + + public void setChecked(boolean checked) { + this.isChecked = checked; + } + } + + private final View.OnClickListener TopButtonsOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!isAdded()) { + return; + } + + //LinearLayout tab = (LinearLayout) v; + TabViewHolder tabViewHolder = (TabViewHolder) v.getTag(); + + if (tabViewHolder.isChecked) { + // already checked. Do nothing + return; + } + + int numberOfTabs = mModuleButtonsContainer.getChildCount(); + int checkedId = -1; + for (int i = 0; i < numberOfTabs; i++) { + LinearLayout currentTab = (LinearLayout) mModuleButtonsContainer.getChildAt(i); + TabViewHolder currentTabViewHolder = (TabViewHolder) currentTab.getTag(); + if (tabViewHolder == currentTab.getTag()) { + checkedId = i; + currentTabViewHolder.setChecked(true); + } else { + currentTabViewHolder.setChecked(false); + } + } + + if (checkedId == -1) + return; + + mSelectedOverviewItemIndex = checkedId; + if (mOverviewItemChangeListener != null) { + mOverviewItemChangeListener.onOverviewItemChanged( + overviewItems[mSelectedOverviewItemIndex] + ); + } + updateUI(); + } + }; + + + private final View.OnClickListener onCheckboxClicked = new View.OnClickListener() { + @Override + public void onClick(View view) { + // Is the view now checked? + mIsCheckboxChecked = ((CheckBox) view).isChecked(); + updateUI(); + } + }; + + + @Override + protected boolean hasDataAvailable() { + return mVisitsData != null; + } + @Override + protected void saveStatsData(Bundle outState) { + if (hasDataAvailable()) { + outState.putSerializable(ARG_REST_RESPONSE, mVisitsData); + } + outState.putInt(ARG_SELECTED_GRAPH_BAR, mSelectedBarGraphBarIndex); + outState.putInt(ARG_PREV_NUMBER_OF_BARS, mPrevNumberOfBarsGraph); + outState.putInt(ARG_SELECTED_OVERVIEW_ITEM, mSelectedOverviewItemIndex); + outState.putBoolean(ARG_CHECKBOX_SELECTED, mVisitorsCheckbox.isChecked()); + } + @Override + protected void restoreStatsData(Bundle savedInstanceState) { + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(ARG_REST_RESPONSE)) { + mVisitsData = (VisitsModel) savedInstanceState.getSerializable(ARG_REST_RESPONSE); + } + if (savedInstanceState.containsKey(ARG_SELECTED_OVERVIEW_ITEM)) { + mSelectedOverviewItemIndex = savedInstanceState.getInt(ARG_SELECTED_OVERVIEW_ITEM, 0); + } + if (savedInstanceState.containsKey(ARG_SELECTED_GRAPH_BAR)) { + mSelectedBarGraphBarIndex = savedInstanceState.getInt(ARG_SELECTED_GRAPH_BAR, -1); + } + if (savedInstanceState.containsKey(ARG_PREV_NUMBER_OF_BARS)) { + mPrevNumberOfBarsGraph = savedInstanceState.getInt(ARG_PREV_NUMBER_OF_BARS, -1); + } + + mIsCheckboxChecked = savedInstanceState.getBoolean(ARG_CHECKBOX_SELECTED, true); + } + } + + @Override + protected void showErrorUI(String label) { + setupNoResultsUI(false); + } + + @Override + protected void showPlaceholderUI() { + setupNoResultsUI(true); + } + + private VisitModel[] getDataToShowOnGraph(VisitsModel visitsData) { + List<VisitModel> visitModels = visitsData.getVisits(); + int numPoints = Math.min(StatsUIHelper.getNumOfBarsToShow(), visitModels.size()); + int currentPointIndex = numPoints - 1; + VisitModel[] visitModelsToShow = new VisitModel[numPoints]; + + for (int i = visitModels.size() -1; i >= 0 && currentPointIndex >= 0; i--) { + VisitModel currentVisitModel = visitModels.get(i); + visitModelsToShow[currentPointIndex] = currentVisitModel; + currentPointIndex--; + } + return visitModelsToShow; + } + + protected void updateUI() { + if (!isAdded()) { + return; + } + + if (mVisitsData == null) { + setupNoResultsUI(false); + return; + } + + final VisitModel[] dataToShowOnGraph = getDataToShowOnGraph(mVisitsData); + if (dataToShowOnGraph == null || dataToShowOnGraph.length == 0) { + setupNoResultsUI(false); + return; + } + + // Hide the "no-activity this period" message + mNoActivtyThisPeriodContainer.setVisibility(View.GONE); + + // Read the selected Tab in the UI + OverviewLabel selectedStatsType = overviewItems[mSelectedOverviewItemIndex]; + + // Update the Legend and enable/disable the visitors checkboxes + mLegendContainer.setVisibility(View.VISIBLE); + mLegendLabel.setText(StringUtils.capitalize(selectedStatsType.getLabel().toLowerCase())); + switch(selectedStatsType) { + case VIEWS: + mVisitorsCheckboxContainer.setVisibility(View.VISIBLE); + mVisitorsCheckbox.setEnabled(true); + mVisitorsCheckbox.setChecked(mIsCheckboxChecked); + break; + default: + mVisitorsCheckboxContainer.setVisibility(View.GONE); + break; + } + + // Setting Up labels and prepare variables that hold series + final String[] horLabels = new String[dataToShowOnGraph.length]; + mStatsDate = new String[dataToShowOnGraph.length]; + GraphView.GraphViewData[] mainSeriesItems = new GraphView.GraphViewData[dataToShowOnGraph.length]; + + GraphView.GraphViewData[] secondarySeriesItems = null; + if (mIsCheckboxChecked && selectedStatsType == OverviewLabel.VIEWS) { + secondarySeriesItems = new GraphView.GraphViewData[dataToShowOnGraph.length]; + } + + // index of days that should be XXX on the graph + final boolean[] weekendDays; + if (getTimeframe() == StatsTimeframe.DAY) { + weekendDays = new boolean[dataToShowOnGraph.length]; + } else { + weekendDays = null; + } + + // Check we have at least one result in the current section. + boolean atLeastOneResultIsAvailable = false; + + // Fill series variables with data + for (int i = 0; i < dataToShowOnGraph.length; i++) { + int currentItemValue = 0; + switch(selectedStatsType) { + case VIEWS: + currentItemValue = dataToShowOnGraph[i].getViews(); + break; + case VISITORS: + currentItemValue = dataToShowOnGraph[i].getVisitors(); + break; + case LIKES: + currentItemValue = dataToShowOnGraph[i].getLikes(); + break; + case COMMENTS: + currentItemValue = dataToShowOnGraph[i].getComments(); + break; + } + mainSeriesItems[i] = new GraphView.GraphViewData(i, currentItemValue); + + if (currentItemValue > 0) { + atLeastOneResultIsAvailable = true; + } + + if (mIsCheckboxChecked && secondarySeriesItems != null) { + secondarySeriesItems[i] = new GraphView.GraphViewData(i, dataToShowOnGraph[i].getVisitors()); + } + + String currentItemStatsDate = dataToShowOnGraph[i].getPeriod(); + horLabels[i] = getDateLabelForBarInGraph(currentItemStatsDate); + mStatsDate[i] = currentItemStatsDate; + + if (weekendDays != null) { + SimpleDateFormat from = new SimpleDateFormat(StatsConstants.STATS_INPUT_DATE_FORMAT); + try { + Date date = from.parse(currentItemStatsDate); + Calendar c = Calendar.getInstance(); + c.setFirstDayOfWeek(Calendar.MONDAY); + c.setTimeInMillis(date.getTime()); + weekendDays[i] = c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY; + } catch (ParseException e) { + weekendDays[i] = false; + AppLog.e(AppLog.T.STATS, e); + } + } + } + + if (mGraphContainer.getChildCount() >= 1 && mGraphContainer.getChildAt(0) instanceof GraphView) { + mGraphView = (StatsBarGraph) mGraphContainer.getChildAt(0); + } else { + mGraphContainer.removeAllViews(); + mGraphView = new StatsBarGraph(getActivity()); + mGraphContainer.addView(mGraphView); + } + + mGraphView.removeAllSeries(); + + GraphViewSeries mainSeriesOnScreen = new GraphViewSeries(mainSeriesItems); + mainSeriesOnScreen.getStyle().color = getResources().getColor(R.color.stats_bar_graph_main_series); + mainSeriesOnScreen.getStyle().outerColor = getResources().getColor(R.color.translucent_grey_lighten_30); + mainSeriesOnScreen.getStyle().highlightColor = getResources().getColor(R.color.stats_bar_graph_main_series_highlight); + mainSeriesOnScreen.getStyle().outerhighlightColor = getResources().getColor(R.color.stats_bar_graph_outer_highlight); + mainSeriesOnScreen.getStyle().padding = DisplayUtils.dpToPx(getActivity(), 5); + mGraphView.addSeries(mainSeriesOnScreen); + + // Add the Visitors series if it's checked in the legend + if (mIsCheckboxChecked && secondarySeriesItems != null && selectedStatsType == OverviewLabel.VIEWS) { + GraphViewSeries secondarySeries = new GraphViewSeries(secondarySeriesItems); + secondarySeries.getStyle().padding = DisplayUtils.dpToPx(getActivity(), 10); + secondarySeries.getStyle().color = getResources().getColor(R.color.stats_bar_graph_secondary_series); + secondarySeries.getStyle().highlightColor = getResources().getColor(R.color.orange_fire); + mGraphView.addSeries(secondarySeries); + } + + // Setup the Y-axis on Visitors and Views Tabs. + // Views and Visitors tabs have the exact same Y-axis as shifting from one Y-axis to another defeats + // the purpose of making these bars visually easily to compare. + switch(selectedStatsType) { + case VISITORS: + double maxYValue = getMaxYValueForVisitorsAndView(dataToShowOnGraph); + mGraphView.setManualYAxisBounds(maxYValue, 0d); + break; + default: + mGraphView.setManualYAxis(false); + break; + } + + // Set the Graph Style + mGraphView.getGraphViewStyle().setNumHorizontalLabels(dataToShowOnGraph.length); + // Set the maximum size a column can get on the screen in PX + mGraphView.getGraphViewStyle().setMaxColumnWidth( + DisplayUtils.dpToPx(getActivity(), StatsConstants.STATS_GRAPH_BAR_MAX_COLUMN_WIDTH_DP) + ); + mGraphView.setHorizontalLabels(horLabels); + mGraphView.setGestureListener(this); + + // If zero results in the current section disable clicks on the graph and show the dialog. + mNoActivtyThisPeriodContainer.setVisibility(atLeastOneResultIsAvailable ? View.GONE : View.VISIBLE); + mGraphView.setClickable(atLeastOneResultIsAvailable); + + // Draw the background on weekend days + mGraphView.setWeekendDays(weekendDays); + + // Reset the bar selected upon rotation of the device when the no. of bars can change with orientation. + // Only happens on 720DP tablets + if (mPrevNumberOfBarsGraph != -1 && mPrevNumberOfBarsGraph != dataToShowOnGraph.length) { + mSelectedBarGraphBarIndex = -1; + mPrevNumberOfBarsGraph = dataToShowOnGraph.length; + onBarTapped(dataToShowOnGraph.length - 1); + mGraphView.highlightBar(dataToShowOnGraph.length - 1); + return; + } + + mPrevNumberOfBarsGraph = dataToShowOnGraph.length; + int barSelectedOnGraph; + if (mSelectedBarGraphBarIndex == -1) { + // No previous bar was highlighted, highlight the most recent one + barSelectedOnGraph = dataToShowOnGraph.length - 1; + } else if (mSelectedBarGraphBarIndex < dataToShowOnGraph.length) { + barSelectedOnGraph = mSelectedBarGraphBarIndex; + } else { + // A previous bar was highlighted, but it's out of the screen now. This should never happen atm. + barSelectedOnGraph = dataToShowOnGraph.length - 1; + mSelectedBarGraphBarIndex = barSelectedOnGraph; + } + + updateUIBelowTheGraph(barSelectedOnGraph); + mGraphView.highlightBar(barSelectedOnGraph); + } + + // Find the max value in Visitors and Views data. + // Only checks the Views data, since Visitors is for sure less-equals than Views. + private double getMaxYValueForVisitorsAndView(final VisitModel[] dataToShowOnGraph) { + if (dataToShowOnGraph == null || dataToShowOnGraph.length == 0) { + return 0d; + } + double largest = Integer.MIN_VALUE; + + for (VisitModel aDataToShowOnGraph : dataToShowOnGraph) { + int currentItemValue = aDataToShowOnGraph.getViews(); + if (currentItemValue > largest) { + largest = currentItemValue; + } + } + return largest; + } + + //update the area right below the graph + private void updateUIBelowTheGraph(int itemPosition) { + if (!isAdded()) { + return; + } + + if (mVisitsData == null) { + setupNoResultsUI(false); + return; + } + + final VisitModel[] dataToShowOnGraph = getDataToShowOnGraph(mVisitsData); + + // Make sure we've data to show on the screen + if (dataToShowOnGraph.length == 0) { + return; + } + + // This check should never be true, since we put a check on the index in the calling function updateUI() + if (dataToShowOnGraph.length <= itemPosition || itemPosition == -1) { + // Make sure we're not highlighting + itemPosition = dataToShowOnGraph.length -1; + } + + String date = mStatsDate[itemPosition]; + if (date == null) { + AppLog.w(AppLog.T.STATS, "Cannot update the area below the graph if a null date is passed!!"); + return; + } + + mDateTextView.setText(getDateForDisplayInLabels(date, getTimeframe())); + + VisitModel modelTapped = dataToShowOnGraph[itemPosition]; + for (int i=0 ; i < mModuleButtonsContainer.getChildCount(); i++) { + View o = mModuleButtonsContainer.getChildAt(i); + if (o instanceof LinearLayout && o.getTag() instanceof TabViewHolder) { + TabViewHolder tabViewHolder = (TabViewHolder)o.getTag(); + int currentValue = 0; + switch (tabViewHolder.labelItem) { + case VIEWS: + currentValue = modelTapped.getViews(); + break; + case VISITORS: + currentValue = modelTapped.getVisitors(); + break; + case LIKES: + currentValue = modelTapped.getLikes(); + break; + case COMMENTS: + currentValue = modelTapped.getComments(); + break; + } + tabViewHolder.value.setText(FormatUtils.formatDecimal(currentValue)); + tabViewHolder.updateBackGroundAndIcon(currentValue); + } + } + } + + private String getDateForDisplayInLabels(String date, StatsTimeframe timeframe) { + String prefix = getString(R.string.stats_for); + switch (timeframe) { + case DAY: + return String.format(prefix, StatsUtils.parseDate(date, StatsConstants.STATS_INPUT_DATE_FORMAT, StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_DAY_SHORT_FORMAT)); + case WEEK: + try { + SimpleDateFormat sdf; + Calendar c; + final Date parsedDate; + // Used in bar graph + // first four digits are the year + // followed by Wxx where xx is the month + // followed by Wxx where xx is the day of the month + // ex: 2013W07W22 = July 22, 2013 + sdf = new SimpleDateFormat("yyyy'W'MM'W'dd"); + //Calculate the end of the week + parsedDate = sdf.parse(date); + c = Calendar.getInstance(); + c.setFirstDayOfWeek(Calendar.MONDAY); + c.setTime(parsedDate); + // first day of this week + c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY ); + String startDateLabel = StatsUtils.msToString(c.getTimeInMillis(), StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_DAY_LONG_FORMAT); + // last day of this week + c.add(Calendar.DAY_OF_WEEK, + 6); + String endDateLabel = StatsUtils.msToString(c.getTimeInMillis(), StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_DAY_LONG_FORMAT); + return String.format(prefix, startDateLabel + " - " + endDateLabel); + } catch (ParseException e) { + AppLog.e(AppLog.T.UTILS, e); + return ""; + } + case MONTH: + return String.format(prefix, StatsUtils.parseDate(date, StatsConstants.STATS_INPUT_DATE_FORMAT, StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_FORMAT)); + case YEAR: + return String.format(prefix, StatsUtils.parseDate(date, StatsConstants.STATS_INPUT_DATE_FORMAT, StatsConstants.STATS_OUTPUT_DATE_YEAR_FORMAT)); + } + return ""; + } + + /** + * Return the date string that is displayed under each bar in the graph + */ + private String getDateLabelForBarInGraph(String dateToFormat) { + switch (getTimeframe()) { + case DAY: + return StatsUtils.parseDate( + dateToFormat, + StatsConstants.STATS_INPUT_DATE_FORMAT, + StatsConstants.STATS_OUTPUT_DATE_MONTH_SHORT_DAY_SHORT_FORMAT + ); + case WEEK: + // first four digits are the year + // followed by Wxx where xx is the month + // followed by Wxx where xx is the day of the month + // ex: 2013W07W22 = July 22, 2013 + return StatsUtils.parseDate(dateToFormat, "yyyy'W'MM'W'dd", StatsConstants.STATS_OUTPUT_DATE_MONTH_SHORT_DAY_SHORT_FORMAT); + case MONTH: + return StatsUtils.parseDate(dateToFormat, "yyyy-MM", "MMM"); + case YEAR: + return StatsUtils.parseDate(dateToFormat, StatsConstants.STATS_INPUT_DATE_FORMAT, StatsConstants.STATS_OUTPUT_DATE_YEAR_FORMAT); + default: + return dateToFormat; + } + } + + private void setupNoResultsUI(boolean isLoading) { + if (!isAdded()) { + return; + } + + // Hide the legend + mLegendContainer.setVisibility(View.GONE); + mVisitorsCheckboxContainer.setVisibility(View.GONE); + + mSelectedBarGraphBarIndex = -1; + Context context = mGraphContainer.getContext(); + if (context != null) { + LayoutInflater inflater = LayoutInflater.from(context); + View emptyBarGraphView = inflater.inflate(R.layout.stats_bar_graph_empty, mGraphContainer, false); + + final TextView emptyLabel = (TextView) emptyBarGraphView.findViewById(R.id.stats_bar_graph_empty_label); + emptyLabel.setText(""); + if (!isLoading) { + mNoActivtyThisPeriodContainer.setVisibility(View.VISIBLE); + } + + if (emptyBarGraphView != null) { + mGraphContainer.removeAllViews(); + mGraphContainer.addView(emptyBarGraphView); + } + } + mDateTextView.setText(""); + + for (int i=0 ; i < mModuleButtonsContainer.getChildCount(); i++) { + View o = mModuleButtonsContainer.getChildAt(i); + if (o instanceof CheckedTextView) { + CheckedTextView currentBtm = (CheckedTextView)o; + OverviewLabel overviewItem = (OverviewLabel)currentBtm.getTag(); + String labelPrefix = overviewItem.getLabel() + "\n 0" ; + currentBtm.setText(labelPrefix); + } + } + } + + @Override + protected String getTitle() { + return getString(R.string.stats_view_visitors_and_views); + } + + @SuppressWarnings("unused") + public void onEventMainThread(StatsEvents.VisitorsAndViewsUpdated event) { + if (!shouldUpdateFragmentOnUpdateEvent(event)) { + return; + } + + mVisitsData = event.mVisitsAndViews; + mSelectedBarGraphBarIndex = -1; + + // Reset the bar to highlight + if (mGraphView != null) { + mGraphView.resetHighlightBar(); + } + + updateUI(); + } + + @SuppressWarnings("unused") + public void onEventMainThread(StatsEvents.SectionUpdateError event) { + if (!shouldUpdateFragmentOnErrorEvent(event)) { + return; + } + + mVisitsData = null; + mSelectedBarGraphBarIndex = -1; + + // Reset the bar to highlight + if (mGraphView != null) { + mGraphView.resetHighlightBar(); + } + + updateUI(); + } + + @Override + public void onBarTapped(int tappedBar) { + if (!isAdded()) { + return; + } + //AppLog.d(AppLog.T.STATS, " Tapped bar date " + mStatsDate[tappedBar]); + mSelectedBarGraphBarIndex = tappedBar; + updateUIBelowTheGraph(tappedBar); + + if (!NetworkUtils.checkConnection(getActivity())) { + return; + } + + // Update Stats here + String date = mStatsDate[tappedBar]; + if (date == null) { + AppLog.w(AppLog.T.STATS, "A bar was tapped but a null date is received!!"); + return; + } + + //Calculate the correct end date for the selected period + String calculatedDate = null; + + try { + SimpleDateFormat sdf; + Calendar c = Calendar.getInstance(); + c.setFirstDayOfWeek(Calendar.MONDAY); + final Date parsedDate; + switch (getTimeframe()) { + case DAY: + calculatedDate = date; + break; + case WEEK: + // first four digits are the year + // followed by Wxx where xx is the month + // followed by Wxx where xx is the day of the month + // ex: 2013W07W22 = July 22, 2013 + sdf = new SimpleDateFormat("yyyy'W'MM'W'dd"); + //Calculate the end of the week + parsedDate = sdf.parse(date); + c.setTime(parsedDate); + // first day of this week + c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + // last day of this week + c.add(Calendar.DAY_OF_WEEK, +6); + calculatedDate = StatsUtils.msToString(c.getTimeInMillis(), StatsConstants.STATS_INPUT_DATE_FORMAT); + break; + case MONTH: + sdf = new SimpleDateFormat("yyyy-MM"); + //Calculate the end of the month + parsedDate = sdf.parse(date); + c.setTime(parsedDate); + // last day of this month + c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH)); + calculatedDate = StatsUtils.msToString(c.getTimeInMillis(), StatsConstants.STATS_INPUT_DATE_FORMAT); + break; + case YEAR: + sdf = new SimpleDateFormat(StatsConstants.STATS_INPUT_DATE_FORMAT); + //Calculate the end of the week + parsedDate = sdf.parse(date); + c.setTime(parsedDate); + c.set(Calendar.MONTH, Calendar.DECEMBER); + c.set(Calendar.DAY_OF_MONTH, 31); + calculatedDate = StatsUtils.msToString(c.getTimeInMillis(), StatsConstants.STATS_INPUT_DATE_FORMAT); + break; + } + } catch (ParseException e) { + AppLog.e(AppLog.T.UTILS, e); + } + + if (calculatedDate == null) { + AppLog.w(AppLog.T.STATS, "A call to request new stats stats is made but date received cannot be parsed!! " + date); + return; + } + + // Update the data below the graph + if (mListener!= null) { + // Should never be null + final Blog currentBlog = WordPress.getBlog(getLocalTableBlogID()); + if (currentBlog != null && currentBlog.getDotComBlogId() != null) { + mListener.onDateChanged(currentBlog.getDotComBlogId(), getTimeframe(), calculatedDate); + } + } + + AnalyticsUtils.trackWithBlogDetails( + AnalyticsTracker.Stat.STATS_TAPPED_BAR_CHART, + WordPress.getBlog(getLocalTableBlogID()) + ); + } + + public enum OverviewLabel { + VIEWS(R.string.stats_views), + VISITORS(R.string.stats_visitors), + LIKES(R.string.stats_likes), + COMMENTS(R.string.stats_comments), + ; + + private final int mLabelResId; + + OverviewLabel(int labelResId) { + mLabelResId = labelResId; + } + + public String getLabel() { + return WordPress.getContext().getString(mLabelResId).toUpperCase(); + } + } + + @Override + protected StatsService.StatsEndpointsEnum[] sectionsToUpdate() { + return new StatsService.StatsEndpointsEnum[]{ + StatsService.StatsEndpointsEnum.VISITS + }; + } +} |