aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java')
-rw-r--r--WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java907
1 files changed, 907 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java
new file mode 100644
index 000000000..0ec8970cd
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/StatsSingleItemDetailsActivity.java
@@ -0,0 +1,907 @@
+package org.wordpress.android.ui.stats;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.util.SparseBooleanArray;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.volley.NoConnectionError;
+import com.android.volley.VolleyError;
+import com.jjoe64.graphview.GraphView;
+import com.jjoe64.graphview.GraphViewSeries;
+import com.wordpress.rest.RestRequest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.analytics.AnalyticsTracker;
+import org.wordpress.android.networking.RestClientUtils;
+import org.wordpress.android.ui.ActivityId;
+import org.wordpress.android.ui.stats.models.PostViewsModel;
+import org.wordpress.android.ui.stats.models.VisitModel;
+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.ToastUtils;
+import org.wordpress.android.util.helpers.SwipeToRefreshHelper;
+import org.wordpress.android.util.widgets.CustomSwipeRefreshLayout;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+
+/**
+ * Single item details activity.
+ */
+public class StatsSingleItemDetailsActivity extends AppCompatActivity
+ implements StatsBarGraph.OnGestureListener{
+
+ public static final String ARG_REMOTE_BLOG_ID = "ARG_REMOTE_BLOG_ID";
+ public static final String ARG_REMOTE_ITEM_ID = "ARG_REMOTE_ITEM_ID";
+ public static final String ARG_REMOTE_ITEM_TYPE = "ARG_REMOTE_ITEM_TYPE";
+ public static final String ARG_ITEM_TITLE = "ARG_ITEM_TITLE";
+ public static final String ARG_ITEM_URL = "ARG_ITEM_URL";
+ private static final String ARG_REST_RESPONSE = "ARG_REST_RESPONSE";
+ 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 SAVED_STATS_SCROLL_POSITION = "SAVED_STATS_SCROLL_POSITION";
+
+ private boolean mIsUpdatingStats;
+ private SwipeToRefreshHelper mSwipeToRefreshHelper;
+ private ScrollViewExt mOuterScrollView;
+
+ private final Handler mHandler = new Handler();
+
+ private LinearLayout mGraphContainer;
+ private TextView mStatsViewsLabel;
+ private TextView mStatsViewsTotals;
+
+ private LinearLayout mMonthsAndYearsModule;
+ private LinearLayout mMonthsAndYearsList;
+ private RelativeLayout mMonthsAndYearsHeader;
+ private LinearLayout mMonthsAndYearsEmptyPlaceholder;
+
+ private LinearLayout mAveragesModule;
+ private LinearLayout mAveragesList;
+ private RelativeLayout mAveragesHeader;
+ private LinearLayout mAveragesEmptyPlaceholder;
+
+ private LinearLayout mRecentWeeksModule;
+ private LinearLayout mRecentWeeksList;
+ private RelativeLayout mRecentWeeksHeader;
+ private LinearLayout mRecentWeeksEmptyPlaceholder;
+ private String mRemoteBlogID, mRemoteItemID, mRemoteItemType, mItemTitle, mItemURL;
+ private PostViewsModel mRestResponseParsed;
+ private int mSelectedBarGraphIndex = -1;
+ private int mPrevNumberOfBarsGraph = -1;
+
+ private SparseBooleanArray mYearsIdToExpandedMap;
+ private SparseBooleanArray mAveragesIdToExpandedMap;
+ private SparseBooleanArray mRecentWeeksIdToExpandedMap;
+
+ private static final String ARG_YEARS_EXPANDED_ROWS = "ARG_YEARS_EXPANDED_ROWS";
+ private static final String ARG_AVERAGES_EXPANDED_ROWS = "ARG_AVERAGES_EXPANDED_ROWS";
+ private static final String ARG_RECENT_EXPANDED_ROWS = "ARG_RECENT_EXPANDED_ROWS";
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.stats_activity_single_post_details);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ // pull to refresh setup
+ mSwipeToRefreshHelper = new SwipeToRefreshHelper(this, (CustomSwipeRefreshLayout) findViewById(R.id.ptr_layout),
+ new SwipeToRefreshHelper.RefreshListener() {
+ @Override
+ public void onRefreshStarted() {
+ if (!NetworkUtils.checkConnection(getBaseContext())) {
+ mSwipeToRefreshHelper.setRefreshing(false);
+ return;
+ }
+ refreshStats();
+ }
+ }
+ );
+
+ TextView mStatsForLabel = (TextView) findViewById(R.id.stats_summary_title);
+ mGraphContainer = (LinearLayout) findViewById(R.id.stats_bar_chart_fragment_container);
+ mStatsViewsLabel = (TextView) findViewById(R.id.stats_views_label);
+ mStatsViewsTotals = (TextView) findViewById(R.id.stats_views_totals);
+
+ mMonthsAndYearsModule = (LinearLayout) findViewById(R.id.stats_months_years_module);
+ mMonthsAndYearsHeader = (RelativeLayout) findViewById(R.id.stats_months_years_header);
+ mMonthsAndYearsList = (LinearLayout) findViewById(R.id.stats_months_years_list_linearlayout);
+ mMonthsAndYearsEmptyPlaceholder = (LinearLayout) findViewById(R.id.stats_months_years_empty_module_placeholder);
+
+ mAveragesModule = (LinearLayout) findViewById(R.id.stats_averages_module);
+ mAveragesHeader = (RelativeLayout) findViewById(R.id.stats_averages_list_header);
+ mAveragesList = (LinearLayout) findViewById(R.id.stats_averages_list_linearlayout);
+ mAveragesEmptyPlaceholder = (LinearLayout) findViewById(R.id.stats_averages_empty_module_placeholder);
+
+ mRecentWeeksModule = (LinearLayout) findViewById(R.id.stats_recent_weeks_module);
+ mRecentWeeksHeader = (RelativeLayout) findViewById(R.id.stats_recent_weeks_list_header);
+ mRecentWeeksList = (LinearLayout) findViewById(R.id.stats_recent_weeks_list_linearlayout);
+ mRecentWeeksEmptyPlaceholder = (LinearLayout) findViewById(R.id.stats_recent_weeks_empty_module_placeholder);
+
+ mYearsIdToExpandedMap = new SparseBooleanArray();
+ mAveragesIdToExpandedMap = new SparseBooleanArray();
+ mRecentWeeksIdToExpandedMap = new SparseBooleanArray();
+
+ setTitle(R.string.stats);
+ mOuterScrollView = (ScrollViewExt) findViewById(R.id.scroll_view_stats);
+
+ if (savedInstanceState != null) {
+ mRemoteItemID = savedInstanceState.getString(ARG_REMOTE_ITEM_ID);
+ mRemoteBlogID = savedInstanceState.getString(ARG_REMOTE_BLOG_ID);
+ mRemoteItemType = savedInstanceState.getString(ARG_REMOTE_ITEM_TYPE);
+ mItemTitle = savedInstanceState.getString(ARG_ITEM_TITLE);
+ mItemURL = savedInstanceState.getString(ARG_ITEM_URL);
+ mRestResponseParsed = (PostViewsModel) savedInstanceState.getSerializable(ARG_REST_RESPONSE);
+ mSelectedBarGraphIndex = savedInstanceState.getInt(ARG_SELECTED_GRAPH_BAR, -1);
+ mPrevNumberOfBarsGraph = savedInstanceState.getInt(ARG_PREV_NUMBER_OF_BARS, -1);
+
+ final int yScrollPosition = savedInstanceState.getInt(SAVED_STATS_SCROLL_POSITION);
+ if(yScrollPosition != 0) {
+ mOuterScrollView.postDelayed(new Runnable() {
+ public void run() {
+ if (!isFinishing()) {
+ mOuterScrollView.scrollTo(0, yScrollPosition);
+ }
+ }
+ }, StatsConstants.STATS_SCROLL_TO_DELAY);
+ }
+ if (savedInstanceState.containsKey(ARG_AVERAGES_EXPANDED_ROWS)) {
+ mAveragesIdToExpandedMap = savedInstanceState.getParcelable(ARG_AVERAGES_EXPANDED_ROWS);
+ }
+ if (savedInstanceState.containsKey(ARG_RECENT_EXPANDED_ROWS)) {
+ mRecentWeeksIdToExpandedMap = savedInstanceState.getParcelable(ARG_RECENT_EXPANDED_ROWS);
+ }
+ if (savedInstanceState.containsKey(ARG_YEARS_EXPANDED_ROWS)) {
+ mYearsIdToExpandedMap = savedInstanceState.getParcelable(ARG_YEARS_EXPANDED_ROWS);
+ }
+ } else if (getIntent() != null && getIntent().getExtras() != null) {
+ Bundle extras = getIntent().getExtras();
+ mRemoteItemID = extras.getString(ARG_REMOTE_ITEM_ID);
+ mRemoteBlogID = extras.getString(ARG_REMOTE_BLOG_ID);
+ mRemoteItemType = extras.getString(ARG_REMOTE_ITEM_TYPE);
+ mItemTitle = extras.getString(ARG_ITEM_TITLE);
+ mItemURL = extras.getString(ARG_ITEM_URL);
+ mRestResponseParsed = (PostViewsModel) extras.getSerializable(ARG_REST_RESPONSE);
+ mSelectedBarGraphIndex = extras.getInt(ARG_SELECTED_GRAPH_BAR, -1);
+ }
+
+ if (mRemoteBlogID == null || mRemoteItemID == null) {
+ Toast.makeText(this, R.string.stats_generic_error, Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ if (savedInstanceState == null) {
+ AnalyticsUtils.trackWithBlogDetails(
+ AnalyticsTracker.Stat.STATS_SINGLE_POST_ACCESSED,
+ mRemoteBlogID
+ );
+ }
+
+ // Setup the main top label that opens the post in the Reader where possible
+ if (mItemTitle != null || mItemURL != null) {
+ mStatsForLabel.setVisibility(View.VISIBLE);
+ mStatsForLabel.setText(mItemTitle != null ? mItemTitle : mItemURL );
+ // make the label clickable if the URL is available
+ if (mItemURL != null) {
+ mStatsForLabel.setTextColor(getResources().getColor(R.color.stats_link_text_color));
+ mStatsForLabel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Context ctx = v.getContext();
+ StatsUtils.openPostInReaderOrInAppWebview(ctx,
+ mRemoteBlogID,
+ mRemoteItemID,
+ mRemoteItemType,
+ mItemURL);
+ }
+ });
+ } else {
+ mStatsForLabel.setTextColor(getResources().getColor(R.color.grey_darken_20));
+ }
+ } else {
+ mStatsForLabel.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putInt(ARG_SELECTED_GRAPH_BAR, mSelectedBarGraphIndex);
+ outState.putInt(ARG_PREV_NUMBER_OF_BARS, mPrevNumberOfBarsGraph);
+ outState.putString(ARG_REMOTE_BLOG_ID, mRemoteBlogID);
+ outState.putString(ARG_REMOTE_ITEM_ID, mRemoteItemID);
+ outState.putString(ARG_REMOTE_ITEM_TYPE, mRemoteItemType);
+ outState.putString(ARG_ITEM_TITLE, mItemTitle);
+ outState.putString(ARG_ITEM_URL, mItemURL);
+
+ outState.putSerializable(ARG_REST_RESPONSE, mRestResponseParsed);
+ if (mOuterScrollView.getScrollY() != 0) {
+ outState.putInt(SAVED_STATS_SCROLL_POSITION, mOuterScrollView.getScrollY());
+ }
+
+ if (mAveragesIdToExpandedMap.size() > 0){
+ outState.putParcelable(ARG_AVERAGES_EXPANDED_ROWS, new SparseBooleanArrayParcelable(mAveragesIdToExpandedMap));
+ }
+ if (mRecentWeeksIdToExpandedMap.size() > 0) {
+ outState.putParcelable(ARG_RECENT_EXPANDED_ROWS, new SparseBooleanArrayParcelable(mRecentWeeksIdToExpandedMap));
+ }
+ if (mYearsIdToExpandedMap.size() > 0) {
+ outState.putParcelable(ARG_YEARS_EXPANDED_ROWS, new SparseBooleanArrayParcelable(mYearsIdToExpandedMap));
+ }
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mRestResponseParsed == null) {
+ // check if network is available, if not shows the empty UI immediately
+ if (!NetworkUtils.checkConnection(this)) {
+ mSwipeToRefreshHelper.setRefreshing(false);
+ setupEmptyUI();
+ } else {
+ setupEmptyGraph("");
+ showHideEmptyModulesIndicator(true);
+ refreshStats();
+ }
+ } else {
+ updateUI();
+ }
+ ActivityId.trackLastActivity(ActivityId.STATS_POST_DETAILS);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mIsUpdatingStats = false;
+ mSwipeToRefreshHelper.setRefreshing(false);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void refreshStats() {
+
+ if (mIsUpdatingStats) {
+ AppLog.w(AppLog.T.STATS, "stats details are already updating for the following postID "
+ + mRemoteItemID + ", refresh cancelled.");
+ return;
+ }
+
+ if (!NetworkUtils.checkConnection(this)) {
+ mSwipeToRefreshHelper.setRefreshing(false);
+ return;
+ }
+
+ final RestClientUtils restClientUtils = WordPress.getRestClientUtilsV1_1();
+
+
+ // View and visitor counts for a site
+ final String singlePostRestPath = String.format(
+ "/sites/%s/stats/post/%s", mRemoteBlogID, mRemoteItemID);
+
+ AppLog.d(AppLog.T.STATS, "Enqueuing the following Stats request " + singlePostRestPath);
+
+ RestBatchCallListener vListener = new RestBatchCallListener(this);
+ restClientUtils.get(singlePostRestPath, vListener, vListener);
+
+ mIsUpdatingStats = true;
+ mSwipeToRefreshHelper.setRefreshing(true);
+ }
+
+ private void showHideEmptyModulesIndicator(boolean show) {
+ if (isFinishing()) {
+ return;
+ }
+
+ mMonthsAndYearsModule.setVisibility(View.VISIBLE);
+ mRecentWeeksModule.setVisibility(View.VISIBLE);
+ mAveragesModule.setVisibility(View.VISIBLE);
+
+ mMonthsAndYearsHeader.setVisibility(show ? View.GONE : View.VISIBLE);
+ mRecentWeeksHeader.setVisibility(show ? View.GONE : View.VISIBLE);
+ mAveragesHeader.setVisibility(show ? View.GONE : View.VISIBLE);
+
+ mMonthsAndYearsList.setVisibility(show ? View.GONE : View.VISIBLE);
+ mAveragesList.setVisibility(show ? View.GONE : View.VISIBLE);
+ mRecentWeeksList.setVisibility(show ? View.GONE : View.VISIBLE);
+
+ mMonthsAndYearsEmptyPlaceholder.setVisibility(show ? View.VISIBLE : View.GONE);
+ mRecentWeeksEmptyPlaceholder.setVisibility(show ? View.VISIBLE : View.GONE);
+ mAveragesEmptyPlaceholder.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ private void setupEmptyUI() {
+ if (isFinishing()) {
+ return;
+ }
+
+ setupEmptyGraph(null);
+
+ mMonthsAndYearsModule.setVisibility(View.GONE);
+ mRecentWeeksModule.setVisibility(View.GONE);
+ mAveragesModule.setVisibility(View.GONE);
+
+ mRecentWeeksIdToExpandedMap.clear();
+ mAveragesIdToExpandedMap.clear();
+ mYearsIdToExpandedMap.clear();
+ }
+
+ private void setupEmptyGraph(String emptyLabel) {
+ if (isFinishing()) {
+ return;
+ }
+ Context context = mGraphContainer.getContext();
+ if (context != null) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ View emptyBarGraphView = inflater.inflate(R.layout.stats_bar_graph_empty, mGraphContainer, false);
+ if (emptyLabel != null) {
+ final TextView emptyLabelField = (TextView) emptyBarGraphView.findViewById(R.id.stats_bar_graph_empty_label);
+ emptyLabelField.setText(emptyLabel);
+ }
+ mGraphContainer.removeAllViews();
+ mGraphContainer.addView(emptyBarGraphView);
+ }
+ mStatsViewsLabel.setText("");
+ mStatsViewsTotals.setText("");
+ }
+
+ private VisitModel[] getDataToShowOnGraph () {
+ if (mRestResponseParsed == null) {
+ return new VisitModel[0];
+ }
+
+ final VisitModel[] dayViews = mRestResponseParsed.getDayViews();
+ if (dayViews == null) {
+ return new VisitModel[0];
+ }
+
+ int numPoints = Math.min(StatsUIHelper.getNumOfBarsToShow(), dayViews.length);
+ int currentPointIndex = numPoints - 1;
+ VisitModel[] visitModels = new VisitModel[numPoints];
+
+ for (int i = dayViews.length - 1; i >= 0 && currentPointIndex >= 0; i--) {
+ visitModels[currentPointIndex] = dayViews[i];
+ currentPointIndex--;
+ }
+
+ return visitModels;
+ }
+
+ private void updateUI() {
+ if (isFinishing()) {
+ return;
+ }
+ final VisitModel[] dataToShowOnGraph = getDataToShowOnGraph();
+
+ if (dataToShowOnGraph == null || dataToShowOnGraph.length == 0) {
+ setupEmptyUI();
+ return;
+ }
+
+ final String[] horLabels = new String[dataToShowOnGraph.length];
+ String[] mStatsDate = new String[dataToShowOnGraph.length];
+ GraphView.GraphViewData[] views = new GraphView.GraphViewData[dataToShowOnGraph.length];
+
+ for (int i = 0; i < dataToShowOnGraph.length; i++) {
+ int currentItemValue = dataToShowOnGraph[i].getViews();
+ views[i] = new GraphView.GraphViewData(i, currentItemValue);
+
+ String currentItemStatsDate = dataToShowOnGraph[i].getPeriod();
+ horLabels[i] = StatsUtils.parseDate(
+ currentItemStatsDate,
+ StatsConstants.STATS_INPUT_DATE_FORMAT,
+ StatsConstants.STATS_OUTPUT_DATE_MONTH_SHORT_DAY_SHORT_FORMAT
+ );
+ mStatsDate[i] = currentItemStatsDate;
+ }
+
+ GraphViewSeries mCurrentSeriesOnScreen = new GraphViewSeries(views);
+ mCurrentSeriesOnScreen.getStyle().color = getResources().getColor(R.color.stats_bar_graph_main_series);
+ mCurrentSeriesOnScreen.getStyle().highlightColor = getResources().getColor(R.color.stats_bar_graph_main_series_highlight);
+ mCurrentSeriesOnScreen.getStyle().outerhighlightColor = getResources().getColor(R.color.stats_bar_graph_outer_highlight);
+ mCurrentSeriesOnScreen.getStyle().padding = DisplayUtils.dpToPx(this, 5);
+
+ StatsBarGraph mGraphView;
+ if (mGraphContainer.getChildCount() >= 1 && mGraphContainer.getChildAt(0) instanceof GraphView) {
+ mGraphView = (StatsBarGraph) mGraphContainer.getChildAt(0);
+ } else {
+ mGraphContainer.removeAllViews();
+ mGraphView = new StatsBarGraph(this);
+ mGraphContainer.addView(mGraphView);
+ }
+
+
+ mGraphView.removeAllSeries();
+ mGraphView.addSeries(mCurrentSeriesOnScreen);
+ //mGraphView.getGraphViewStyle().setNumHorizontalLabels(getNumOfHorizontalLabels(dataToShowOnGraph.length));
+ mGraphView.getGraphViewStyle().setNumHorizontalLabels(dataToShowOnGraph.length);
+ mGraphView.getGraphViewStyle().setMaxColumnWidth(
+ DisplayUtils.dpToPx(this, StatsConstants.STATS_GRAPH_BAR_MAX_COLUMN_WIDTH_DP)
+ );
+ mGraphView.setHorizontalLabels(horLabels);
+ mGraphView.setGestureListener(this);
+
+ // 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) {
+ mSelectedBarGraphIndex = dataToShowOnGraph.length - 1;
+ } else {
+ mSelectedBarGraphIndex = (mSelectedBarGraphIndex != -1) ? mSelectedBarGraphIndex : dataToShowOnGraph.length - 1;
+ }
+
+ mGraphView.highlightBar(mSelectedBarGraphIndex);
+ mPrevNumberOfBarsGraph = dataToShowOnGraph.length;
+
+ setMainViewsLabel(
+ StatsUtils.parseDate(
+ mStatsDate[mSelectedBarGraphIndex],
+ StatsConstants.STATS_INPUT_DATE_FORMAT,
+ StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_DAY_SHORT_FORMAT
+ ),
+ dataToShowOnGraph[mSelectedBarGraphIndex].getViews()
+ );
+
+ showHideEmptyModulesIndicator(false);
+
+ mMonthsAndYearsList.setVisibility(View.VISIBLE);
+ List<PostViewsModel.Year> years = mRestResponseParsed.getYears();
+ MonthsAndYearsListAdapter monthsAndYearsListAdapter = new MonthsAndYearsListAdapter(this, years, mRestResponseParsed.getHighestMonth());
+ StatsUIHelper.reloadGroupViews(this, monthsAndYearsListAdapter, mYearsIdToExpandedMap, mMonthsAndYearsList);
+
+ mAveragesList.setVisibility(View.VISIBLE);
+ List<PostViewsModel.Year> averages = mRestResponseParsed.getAverages();
+ MonthsAndYearsListAdapter averagesListAdapter = new MonthsAndYearsListAdapter(this, averages, mRestResponseParsed.getHighestDayAverage());
+ StatsUIHelper.reloadGroupViews(this, averagesListAdapter, mAveragesIdToExpandedMap, mAveragesList);
+
+ mRecentWeeksList.setVisibility(View.VISIBLE);
+ List<PostViewsModel.Week> recentWeeks = mRestResponseParsed.getWeeks();
+ RecentWeeksListAdapter recentWeeksListAdapter = new RecentWeeksListAdapter(this, recentWeeks, mRestResponseParsed.getHighestWeekAverage());
+ StatsUIHelper.reloadGroupViews(this, recentWeeksListAdapter, mRecentWeeksIdToExpandedMap, mRecentWeeksList);
+ }
+
+
+ private void setMainViewsLabel(String dateFormatted, int totals) {
+ mStatsViewsLabel.setText(getString(R.string.stats_views) + ": "
+ + dateFormatted);
+ mStatsViewsTotals.setText(FormatUtils.formatDecimal(totals));
+ }
+
+
+ private class RecentWeeksListAdapter extends BaseExpandableListAdapter {
+ public static final String GROUP_DATE_FORMAT = "MMM dd";
+ public final LayoutInflater inflater;
+ private final List<PostViewsModel.Week> groups;
+ private final int maxReachedValue;
+
+ public RecentWeeksListAdapter(Context context, List<PostViewsModel.Week> groups, int maxReachedValue) {
+ this.groups = groups;
+ this.inflater = LayoutInflater.from(context);
+ this.maxReachedValue = maxReachedValue;
+ }
+
+ @Override
+ public Object getChild(int groupPosition, int childPosition) {
+ PostViewsModel.Week currentWeek = groups.get(groupPosition);
+ return currentWeek.getDays().get(childPosition);
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ return 0;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, final int childPosition,
+ boolean isLastChild, View convertView, ViewGroup parent) {
+
+ final PostViewsModel.Day currentDay = (PostViewsModel.Day) getChild(groupPosition, childPosition);
+
+ final StatsViewHolder holder;
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.stats_list_cell, parent, false);
+ holder = new StatsViewHolder(convertView);
+ convertView.setTag(holder);
+ } else {
+ holder = (StatsViewHolder) convertView.getTag();
+ }
+
+ holder.setEntryText(StatsUtils.parseDate(currentDay.getDay(), StatsConstants.STATS_INPUT_DATE_FORMAT, "EEE, MMM dd"));
+
+ // Intercept clicks at row level and eat the event. We don't want to show the ripple here.
+ holder.rowContent.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+
+ }
+ });
+ holder.rowContent.setBackgroundColor(Color.TRANSPARENT);
+
+ // totals
+ holder.totalsTextView.setText(FormatUtils.formatDecimal(currentDay.getCount()));
+
+ // show the trophy indicator if the value is the maximum reached
+ if (currentDay.getCount() == maxReachedValue && maxReachedValue > 0) {
+ holder.imgMore.setVisibility(View.VISIBLE);
+ holder.imgMore.setImageDrawable(getResources().getDrawable(R.drawable.stats_icon_trophy));
+ holder.imgMore.setBackgroundColor(Color.TRANSPARENT); // Hide the default click indicator
+ } else {
+ holder.imgMore.setVisibility(View.GONE);
+ }
+
+ holder.networkImageView.setVisibility(View.GONE);
+ return convertView;
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ PostViewsModel.Week week = groups.get(groupPosition);
+ return week.getDays().size();
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return groups.get(groupPosition);
+ }
+
+ @Override
+ public int getGroupCount() {
+ return groups.size();
+ }
+
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return 0;
+ }
+
+ @Override
+ public View getGroupView(final int groupPosition, boolean isExpanded,
+ View convertView, ViewGroup parent) {
+
+ final StatsViewHolder holder;
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.stats_list_cell, parent, false);
+ holder = new StatsViewHolder(convertView);
+ convertView.setTag(holder);
+ } else {
+ holder = (StatsViewHolder) convertView.getTag();
+ }
+
+ PostViewsModel.Week week = (PostViewsModel.Week) getGroup(groupPosition);
+
+ int total = week.getTotal();
+
+ // change the color of the text if one of its childs has reached maximum value
+ int numberOfChilds = getChildrenCount(groupPosition);
+ boolean shouldShowTheTrophyIcon = false;
+ if (maxReachedValue > 0) {
+ for (int i = 0; i < numberOfChilds; i++) {
+ PostViewsModel.Day currentChild = (PostViewsModel.Day) getChild(groupPosition, i);
+ if (currentChild.getCount() == maxReachedValue) {
+ shouldShowTheTrophyIcon = true;
+ }
+ }
+ }
+
+ // Build the label to show on the group
+ String name;
+ PostViewsModel.Day firstChild = (PostViewsModel.Day) getChild(groupPosition, 0);
+ if (numberOfChilds > 1) {
+ PostViewsModel.Day lastChild = (PostViewsModel.Day) getChild(groupPosition, getChildrenCount(groupPosition) - 1);
+ name = StatsUtils.parseDate(firstChild.getDay(), StatsConstants.STATS_INPUT_DATE_FORMAT, GROUP_DATE_FORMAT)
+ + " - " + StatsUtils.parseDate(lastChild.getDay(), StatsConstants.STATS_INPUT_DATE_FORMAT, GROUP_DATE_FORMAT);
+ } else {
+ name = StatsUtils.parseDate(firstChild.getDay(), StatsConstants.STATS_INPUT_DATE_FORMAT, GROUP_DATE_FORMAT);
+ }
+
+ holder.setEntryText(name, getResources().getColor(R.color.stats_link_text_color));
+
+ holder.networkImageView.setVisibility(View.GONE);
+
+ // totals
+ holder.totalsTextView.setText(FormatUtils.formatDecimal(total));
+ if (shouldShowTheTrophyIcon) {
+ holder.imgMore.setVisibility(View.VISIBLE);
+ holder.imgMore.setImageDrawable(getResources().getDrawable(R.drawable.stats_icon_trophy));
+ holder.imgMore.setBackgroundColor(Color.TRANSPARENT); // Hide the default click indicator
+ } else {
+ holder.imgMore.setVisibility(View.GONE);
+ }
+
+ // expand/collapse chevron
+ holder.chevronImageView.setVisibility(numberOfChilds > 0 ? View.VISIBLE : View.GONE);
+ return convertView;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return false;
+ }
+
+ }
+
+
+ private class MonthsAndYearsListAdapter extends BaseExpandableListAdapter {
+ public final LayoutInflater inflater;
+ private final List<PostViewsModel.Year> groups;
+ private final int maxReachedValue;
+
+ public MonthsAndYearsListAdapter(Context context, List<PostViewsModel.Year> groups, int maxReachedValue) {
+ this.groups = groups;
+ this.inflater = LayoutInflater.from(context);
+ this.maxReachedValue = maxReachedValue;
+ }
+
+ @Override
+ public Object getChild(int groupPosition, int childPosition) {
+ PostViewsModel.Year currentYear = groups.get(groupPosition);
+ return currentYear.getMonths().get(childPosition);
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ return 0;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, final int childPosition,
+ boolean isLastChild, View convertView, ViewGroup parent) {
+
+ final PostViewsModel.Month currentMonth = (PostViewsModel.Month) getChild(groupPosition, childPosition);
+
+ final StatsViewHolder holder;
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.stats_list_cell, parent, false);
+ holder = new StatsViewHolder(convertView);
+ convertView.setTag(holder);
+ } else {
+ holder = (StatsViewHolder) convertView.getTag();
+ }
+
+ holder.setEntryText(StatsUtils.parseDate(currentMonth.getMonth(), "MM", StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_FORMAT));
+
+ // Intercept clicks at row level and eat the event. We don't want to show the ripple here.
+ holder.rowContent.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+
+ }
+ });
+ holder.rowContent.setBackgroundColor(Color.TRANSPARENT);
+
+ // totals
+ holder.totalsTextView.setText(FormatUtils.formatDecimal(currentMonth.getCount()));
+
+ // show the trophy indicator if the value is the maximum reached
+ if (currentMonth.getCount() == maxReachedValue && maxReachedValue > 0) {
+ holder.imgMore.setVisibility(View.VISIBLE);
+ holder.imgMore.setImageDrawable(getResources().getDrawable(R.drawable.stats_icon_trophy));
+ holder.imgMore.setBackgroundColor(Color.TRANSPARENT); // Hide the default click indicator
+ } else {
+ holder.imgMore.setVisibility(View.GONE);
+ }
+
+ holder.networkImageView.setVisibility(View.GONE);
+ return convertView;
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ PostViewsModel.Year currentYear = groups.get(groupPosition);
+ return currentYear.getMonths().size();
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return groups.get(groupPosition);
+ }
+
+ @Override
+ public int getGroupCount() {
+ return groups.size();
+ }
+
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return 0;
+ }
+
+ @Override
+ public View getGroupView(final int groupPosition, boolean isExpanded,
+ View convertView, ViewGroup parent) {
+
+ final StatsViewHolder holder;
+ if (convertView == null) {
+ convertView = inflater.inflate(R.layout.stats_list_cell, parent, false);
+ holder = new StatsViewHolder(convertView);
+ convertView.setTag(holder);
+ } else {
+ holder = (StatsViewHolder) convertView.getTag();
+ }
+
+ PostViewsModel.Year year = (PostViewsModel.Year) getGroup(groupPosition);
+
+ String name = year.getLabel();
+ int total = year.getTotal();
+
+ // change the color of the text if one of its childs has reached maximum value
+ int numberOfChilds = getChildrenCount(groupPosition);
+ boolean shouldShowTheTrophyIcon = false;
+ if (maxReachedValue > 0) {
+ for (int i = 0; i < numberOfChilds; i++) {
+ PostViewsModel.Month currentChild = (PostViewsModel.Month) getChild(groupPosition, i);
+ if (currentChild.getCount() == maxReachedValue) {
+ shouldShowTheTrophyIcon = true;
+ break;
+ }
+ }
+ }
+
+ holder.setEntryText(name, getResources().getColor(R.color.stats_link_text_color));
+
+ if (shouldShowTheTrophyIcon) {
+ holder.imgMore.setVisibility(View.VISIBLE);
+ holder.imgMore.setImageDrawable(getResources().getDrawable(R.drawable.stats_icon_trophy));
+ holder.imgMore.setBackgroundColor(Color.TRANSPARENT); // Hide the default click indicator
+ } else {
+ holder.imgMore.setVisibility(View.GONE);
+ }
+
+ // totals
+ holder.totalsTextView.setText(FormatUtils.formatDecimal(total));
+
+ holder.networkImageView.setVisibility(View.GONE);
+
+ // expand/collapse chevron
+ holder.chevronImageView.setVisibility(numberOfChilds > 0 ? View.VISIBLE : View.GONE);
+ return convertView;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return false;
+ }
+
+ }
+
+
+ private class RestBatchCallListener implements RestRequest.Listener, RestRequest.ErrorListener {
+
+ private final WeakReference<Activity> mActivityRef;
+
+ public RestBatchCallListener(Activity activity) {
+ mActivityRef = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void onResponse(final JSONObject response) {
+ if (mActivityRef.get() == null || mActivityRef.get().isFinishing()) {
+ return;
+ }
+ mIsUpdatingStats = false;
+ mSwipeToRefreshHelper.setRefreshing(false);
+ // single background thread used to parse the response in BG.
+ ThreadPoolExecutor parseResponseExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
+ parseResponseExecutor.submit(new Thread() {
+ @Override
+ public void run() {
+ //AppLog.d(AppLog.T.STATS, "The REST response: " + response.toString());
+ mSelectedBarGraphIndex = -1;
+ try {
+ mRestResponseParsed = new PostViewsModel(response);
+ } catch (JSONException e) {
+ AppLog.e(AppLog.T.STATS, "Cannot parse the JSON response", e);
+ resetModelVariables();
+ }
+
+ // Update the UI
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateUI();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onErrorResponse(final VolleyError volleyError) {
+ StatsUtils.logVolleyErrorDetails(volleyError);
+ if (mActivityRef.get() == null || mActivityRef.get().isFinishing()) {
+ return;
+ }
+ resetModelVariables();
+
+ String label = mActivityRef.get().getString(R.string.error_refresh_stats);
+ if (volleyError instanceof NoConnectionError) {
+ label += "\n" + mActivityRef.get().getString(R.string.no_network_message);
+ }
+
+ ToastUtils.showToast(mActivityRef.get(), label, ToastUtils.Duration.LONG);
+ mIsUpdatingStats = false;
+ mSwipeToRefreshHelper.setRefreshing(false);
+
+ // Update the UI
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ updateUI();
+ }
+ });
+ }
+ }
+
+ private void resetModelVariables() {
+ mRestResponseParsed = null;
+ mSelectedBarGraphIndex = -1;
+ mAveragesIdToExpandedMap.clear();
+ mYearsIdToExpandedMap.clear();
+ }
+
+ @Override
+ public void onBarTapped(int tappedBar) {
+ mSelectedBarGraphIndex = tappedBar;
+ final VisitModel[] dataToShowOnGraph = getDataToShowOnGraph();
+ String currentItemStatsDate = dataToShowOnGraph[mSelectedBarGraphIndex].getPeriod();
+ currentItemStatsDate = StatsUtils.parseDate(
+ currentItemStatsDate,
+ StatsConstants.STATS_INPUT_DATE_FORMAT,
+ StatsConstants.STATS_OUTPUT_DATE_MONTH_LONG_DAY_SHORT_FORMAT
+ );
+ setMainViewsLabel(currentItemStatsDate, dataToShowOnGraph[mSelectedBarGraphIndex].getViews());
+ }
+
+}