blob: c3c390e5dadb11fb33b618006645aea9392c5962 [file] [log] [blame]
package org.wordpress.android.ui.stats;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.lang.StringUtils;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.analytics.AnalyticsTracker;
import org.wordpress.android.models.AccountHelper;
import org.wordpress.android.models.Blog;
import org.wordpress.android.ui.ActivityId;
import org.wordpress.android.ui.ActivityLauncher;
import org.wordpress.android.ui.WPWebViewActivity;
import org.wordpress.android.ui.accounts.SignInActivity;
import org.wordpress.android.ui.posts.PromoDialog;
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.util.AnalyticsUtils;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.NetworkUtils;
import org.wordpress.android.util.RateLimitedTask;
import org.wordpress.android.util.ToastUtils;
import org.wordpress.android.util.ToastUtils.Duration;
import org.wordpress.android.util.helpers.SwipeToRefreshHelper;
import org.wordpress.android.util.helpers.SwipeToRefreshHelper.RefreshListener;
import org.wordpress.android.util.widgets.CustomSwipeRefreshLayout;
import org.xmlrpc.android.ApiHelper;
import org.xmlrpc.android.ApiHelper.Method;
import org.xmlrpc.android.XMLRPCCallback;
import org.xmlrpc.android.XMLRPCClientInterface;
import org.xmlrpc.android.XMLRPCFactory;
import java.util.HashMap;
import java.util.Map;
import de.greenrobot.event.EventBus;
/**
* The native stats activity
* <p>
* By pressing a spinner on the action bar, the user can select which timeframe they wish to see.
* </p>
*/
public class StatsActivity extends AppCompatActivity
implements NestedScrollViewExt.ScrollViewListener,
StatsVisitorsAndViewsFragment.OnDateChangeListener,
StatsVisitorsAndViewsFragment.OnOverviewItemChangeListener,
StatsInsightsTodayFragment.OnInsightsTodayClickListener {
private static final String SAVED_WP_LOGIN_STATE = "SAVED_WP_LOGIN_STATE";
private static final String SAVED_STATS_TIMEFRAME = "SAVED_STATS_TIMEFRAME";
private static final String SAVED_STATS_REQUESTED_DATE = "SAVED_STATS_REQUESTED_DATE";
private static final String SAVED_STATS_SCROLL_POSITION = "SAVED_STATS_SCROLL_POSITION";
private static final String SAVED_THERE_WAS_AN_ERROR_LOADING_STATS = "SAVED_THERE_WAS_AN_ERROR_LOADING_STATS";
private Spinner mSpinner;
private NestedScrollViewExt mOuterScrollView;
private static final int REQUEST_JETPACK = 7000;
public static final String ARG_LOCAL_TABLE_BLOG_ID = "ARG_LOCAL_TABLE_BLOG_ID";
public static final String ARG_LAUNCHED_FROM = "ARG_LAUNCHED_FROM";
public static final String ARG_DESIRED_TIMEFRAME = "ARG_DESIRED_TIMEFRAME";
public enum StatsLaunchedFrom {
STATS_WIDGET,
NOTIFICATIONS
}
private int mResultCode = -1;
private boolean mIsInFront;
private int mLocalBlogID = -1;
private StatsTimeframe mCurrentTimeframe = StatsTimeframe.INSIGHTS;
private String mRequestedDate;
private boolean mIsUpdatingStats;
private SwipeToRefreshHelper mSwipeToRefreshHelper;
private TimeframeSpinnerAdapter mTimeframeSpinnerAdapter;
private final StatsTimeframe[] timeframes = {StatsTimeframe.INSIGHTS, StatsTimeframe.DAY, StatsTimeframe.WEEK,
StatsTimeframe.MONTH, StatsTimeframe.YEAR};
private StatsVisitorsAndViewsFragment.OverviewLabel mTabToSelectOnGraph = StatsVisitorsAndViewsFragment.OverviewLabel.VIEWS;
private boolean mThereWasAnErrorLoadingStats = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (WordPress.wpDB == null) {
Toast.makeText(this, R.string.fatal_db_error, Toast.LENGTH_LONG).show();
finish();
return;
}
setContentView(R.layout.stats_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setElevation(0);
actionBar.setTitle(R.string.stats);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
}
mSwipeToRefreshHelper = new SwipeToRefreshHelper(this, (CustomSwipeRefreshLayout) findViewById(R.id.ptr_layout),
new RefreshListener() {
@Override
public void onRefreshStarted() {
if (!NetworkUtils.checkConnection(getBaseContext())) {
mSwipeToRefreshHelper.setRefreshing(false);
return;
}
if (mIsUpdatingStats) {
AppLog.w(T.STATS, "stats are already updating, refresh cancelled");
return;
}
mRequestedDate = StatsUtils.getCurrentDateTZ(mLocalBlogID);
if (checkCredentials()) {
updateTimeframeAndDateAndStartRefreshOfFragments(true);
}
}
});
setTitle(R.string.stats);
mOuterScrollView = (NestedScrollViewExt) findViewById(R.id.scroll_view_stats);
mOuterScrollView.setScrollViewListener(this);
if (savedInstanceState != null) {
mResultCode = savedInstanceState.getInt(SAVED_WP_LOGIN_STATE);
mLocalBlogID = savedInstanceState.getInt(ARG_LOCAL_TABLE_BLOG_ID);
mCurrentTimeframe = (StatsTimeframe) savedInstanceState.getSerializable(SAVED_STATS_TIMEFRAME);
mRequestedDate = savedInstanceState.getString(SAVED_STATS_REQUESTED_DATE);
mThereWasAnErrorLoadingStats = savedInstanceState.getBoolean(SAVED_THERE_WAS_AN_ERROR_LOADING_STATS);
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);
}
} else if (getIntent() != null) {
mLocalBlogID = getIntent().getIntExtra(ARG_LOCAL_TABLE_BLOG_ID, -1);
if (getIntent().hasExtra(SAVED_STATS_TIMEFRAME)) {
mCurrentTimeframe = (StatsTimeframe) getIntent().getSerializableExtra(SAVED_STATS_TIMEFRAME);
} else if (getIntent().hasExtra(ARG_DESIRED_TIMEFRAME)) {
mCurrentTimeframe = (StatsTimeframe) getIntent().getSerializableExtra(ARG_DESIRED_TIMEFRAME);
} else {
// Read the value from app preferences here. Default to 0 - Insights
mCurrentTimeframe = AppPrefs.getStatsTimeframe();
}
mRequestedDate = StatsUtils.getCurrentDateTZ(mLocalBlogID);
if (getIntent().hasExtra(ARG_LAUNCHED_FROM)) {
StatsLaunchedFrom from = (StatsLaunchedFrom) getIntent().getSerializableExtra(ARG_LAUNCHED_FROM);
if (from == StatsLaunchedFrom.STATS_WIDGET) {
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_WIDGET_TAPPED, WordPress.getBlog(mLocalBlogID));
}
}
}
//Make sure the blog_id passed to this activity is valid and the blog is available within the app
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (currentBlog == null) {
AppLog.e(T.STATS, "The blog with local_blog_id " + mLocalBlogID + " cannot be loaded from the DB.");
Toast.makeText(this, R.string.stats_no_blog, Toast.LENGTH_LONG).show();
finish();
return;
}
// create the fragments without forcing the re-creation. If the activity is restarted fragments can already
// be there, and ready to be displayed without making any network connections. A fragment calls the stats service
// if its internal datamodel is empty.
createFragments(false);
if (mSpinner == null) {
mSpinner = (Spinner) findViewById(R.id.filter_spinner);
mTimeframeSpinnerAdapter = new TimeframeSpinnerAdapter(this, timeframes);
mSpinner.setAdapter(mTimeframeSpinnerAdapter);
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (isFinishing()) {
return;
}
final StatsTimeframe selectedTimeframe = (StatsTimeframe) mTimeframeSpinnerAdapter.getItem(position);
if (mCurrentTimeframe == selectedTimeframe) {
AppLog.d(T.STATS, "The selected TIME FRAME is already active: " + selectedTimeframe.getLabel());
return;
}
AppLog.d(T.STATS, "NEW TIME FRAME : " + selectedTimeframe.getLabel());
mCurrentTimeframe = selectedTimeframe;
AppPrefs.setStatsTimeframe(mCurrentTimeframe);
mRequestedDate = StatsUtils.getCurrentDateTZ(mLocalBlogID);
createFragments(true); // Need to recreate fragment here, since a new timeline was selected.
mSpinner.postDelayed(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
scrollToTop();
}
}
}, StatsConstants.STATS_SCROLL_TO_DELAY);
trackStatsAnalytics();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// nop
}
});
Toolbar spinnerToolbar = (Toolbar) findViewById(R.id.toolbar_filter);
spinnerToolbar.setBackgroundColor(getResources().getColor(R.color.blue_medium));
}
selectCurrentTimeframeInActionBar();
TextView otherRecentStatsMovedLabel = (TextView) findViewById(R.id.stats_other_recent_stats_moved);
otherRecentStatsMovedLabel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i = 0; i < timeframes.length; i++) {
if (timeframes[i] == StatsTimeframe.INSIGHTS) {
mSpinner.setSelection(i);
break;
}
}
mSpinner.postDelayed(new Runnable() {
@Override
public void run() {
if (!isFinishing()) {
scrollToTop();
}
}
}, StatsConstants.STATS_SCROLL_TO_DELAY);
}
});
// Track usage here
if (savedInstanceState == null) {
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_ACCESSED, currentBlog);
trackStatsAnalytics();
}
}
private void trackStatsAnalytics() {
// Track usage here
Blog currentBlog = WordPress.getBlog(mLocalBlogID);
switch (mCurrentTimeframe) {
case INSIGHTS:
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_INSIGHTS_ACCESSED, currentBlog);
break;
case DAY:
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_PERIOD_DAYS_ACCESSED, currentBlog);
break;
case WEEK:
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_PERIOD_WEEKS_ACCESSED, currentBlog);
break;
case MONTH:
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_PERIOD_MONTHS_ACCESSED, currentBlog);
break;
case YEAR:
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.STATS_PERIOD_YEARS_ACCESSED, currentBlog);
break;
}
}
@Override
protected void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onResume() {
super.onResume();
mIsInFront = true;
if (NetworkUtils.checkConnection(this)) {
checkCredentials();
} else {
mSwipeToRefreshHelper.setRefreshing(false);
}
ActivityId.trackLastActivity(ActivityId.STATS);
}
@Override
protected void onPause() {
super.onPause();
mIsInFront = false;
mIsUpdatingStats = false;
mSwipeToRefreshHelper.setRefreshing(false);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SAVED_WP_LOGIN_STATE, mResultCode);
outState.putInt(ARG_LOCAL_TABLE_BLOG_ID, mLocalBlogID);
outState.putSerializable(SAVED_STATS_TIMEFRAME, mCurrentTimeframe);
outState.putString(SAVED_STATS_REQUESTED_DATE, mRequestedDate);
outState.putBoolean(SAVED_THERE_WAS_AN_ERROR_LOADING_STATS, mThereWasAnErrorLoadingStats);
if (mOuterScrollView.getScrollY() != 0) {
outState.putInt(SAVED_STATS_SCROLL_POSITION, mOuterScrollView.getScrollY());
}
super.onSaveInstanceState(outState);
}
private void createFragments(boolean forceRecreationOfFragments) {
if (isFinishing()) {
return;
}
// Make the labels invisible see: https://github.com/wordpress-mobile/WordPress-Android/issues/3279
findViewById(R.id.stats_other_recent_stats_label_insights).setVisibility(View.INVISIBLE);
findViewById(R.id.stats_other_recent_stats_label_timeline).setVisibility(View.INVISIBLE);
findViewById(R.id.stats_other_recent_stats_moved).setVisibility(View.INVISIBLE);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
StatsAbstractFragment fragment;
if (mCurrentTimeframe != StatsTimeframe.INSIGHTS) {
findViewById(R.id.stats_timeline_fragments_container).setVisibility(View.VISIBLE);
findViewById(R.id.stats_insights_fragments_container).setVisibility(View.GONE);
if (fm.findFragmentByTag(StatsVisitorsAndViewsFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newVisitorsAndViewsInstance(StatsViewType.GRAPH_AND_SUMMARY, mLocalBlogID, mCurrentTimeframe, mRequestedDate,
mTabToSelectOnGraph);
ft.replace(R.id.stats_visitors_and_views_container, fragment, StatsVisitorsAndViewsFragment.TAG);
}
if (fm.findFragmentByTag(StatsTopPostsAndPagesFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.TOP_POSTS_AND_PAGES, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_top_posts_container, fragment, StatsTopPostsAndPagesFragment.TAG);
}
if (fm.findFragmentByTag(StatsReferrersFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.REFERRERS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_referrers_container, fragment, StatsReferrersFragment.TAG);
}
if (fm.findFragmentByTag(StatsClicksFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.CLICKS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_clicks_container, fragment, StatsClicksFragment.TAG);
}
if (fm.findFragmentByTag(StatsGeoviewsFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.GEOVIEWS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_geoviews_container, fragment, StatsGeoviewsFragment.TAG);
}
if (fm.findFragmentByTag(StatsAuthorsFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.AUTHORS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_top_authors_container, fragment, StatsAuthorsFragment.TAG);
}
if (fm.findFragmentByTag(StatsVideoplaysFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.VIDEO_PLAYS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_video_container, fragment, StatsVideoplaysFragment.TAG);
}
if (fm.findFragmentByTag(StatsSearchTermsFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.SEARCH_TERMS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_search_terms_container, fragment, StatsSearchTermsFragment.TAG);
}
} else {
findViewById(R.id.stats_timeline_fragments_container).setVisibility(View.GONE);
findViewById(R.id.stats_insights_fragments_container).setVisibility(View.VISIBLE);
if (fm.findFragmentByTag(StatsInsightsMostPopularFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.INSIGHTS_MOST_POPULAR, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_insights_most_popular_container, fragment, StatsInsightsMostPopularFragment.TAG);
}
if (fm.findFragmentByTag(StatsInsightsAllTimeFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.INSIGHTS_ALL_TIME, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_insights_all_time_container, fragment, StatsInsightsAllTimeFragment.TAG);
}
if (fm.findFragmentByTag(StatsInsightsTodayFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.INSIGHTS_TODAY, mLocalBlogID, StatsTimeframe.DAY, mRequestedDate);
ft.replace(R.id.stats_insights_today_container, fragment, StatsInsightsTodayFragment.TAG);
}
if (fm.findFragmentByTag(StatsInsightsLatestPostSummaryFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.INSIGHTS_LATEST_POST_SUMMARY, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_insights_latest_post_summary_container, fragment, StatsInsightsLatestPostSummaryFragment.TAG);
}
if (fm.findFragmentByTag(StatsCommentsFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.COMMENTS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_comments_container, fragment, StatsCommentsFragment.TAG);
}
if (fm.findFragmentByTag(StatsTagsAndCategoriesFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.TAGS_AND_CATEGORIES, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_tags_and_categories_container, fragment, StatsTagsAndCategoriesFragment.TAG);
}
if (fm.findFragmentByTag(StatsPublicizeFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.PUBLICIZE, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_publicize_container, fragment, StatsPublicizeFragment.TAG);
}
if (fm.findFragmentByTag(StatsFollowersFragment.TAG) == null || forceRecreationOfFragments) {
fragment = StatsAbstractFragment.newInstance(StatsViewType.FOLLOWERS, mLocalBlogID, mCurrentTimeframe, mRequestedDate);
ft.replace(R.id.stats_followers_container, fragment, StatsFollowersFragment.TAG);
}
}
ft.commitAllowingStateLoss();
// Slightly delayed labels setup: see https://github.com/wordpress-mobile/WordPress-Android/issues/3279
mOuterScrollView.postDelayed(new Runnable() {
@Override
public void run() {
if (isFinishing()) {
return;
}
boolean isInsights = mCurrentTimeframe == StatsTimeframe.INSIGHTS;
findViewById(R.id.stats_other_recent_stats_label_insights).setVisibility(isInsights ? View.VISIBLE : View.GONE);
findViewById(R.id.stats_other_recent_stats_label_timeline).setVisibility(isInsights ? View.GONE : View.VISIBLE);
findViewById(R.id.stats_other_recent_stats_moved).setVisibility(isInsights ? View.GONE : View.VISIBLE);
}
}, StatsConstants.STATS_LABELS_SETUP_DELAY);
}
private void updateTimeframeAndDateAndStartRefreshOfFragments(boolean includeGraph) {
if (isFinishing()) {
return;
}
FragmentManager fm = getFragmentManager();
if (mCurrentTimeframe != StatsTimeframe.INSIGHTS) {
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsTopPostsAndPagesFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsReferrersFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsClicksFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsGeoviewsFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsAuthorsFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsVideoplaysFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsSearchTermsFragment.TAG);
if (includeGraph) {
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsVisitorsAndViewsFragment.TAG);
}
} else {
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsInsightsTodayFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsInsightsAllTimeFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsInsightsMostPopularFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsInsightsLatestPostSummaryFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsCommentsFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsTagsAndCategoriesFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsPublicizeFragment.TAG);
updateTimeframeAndDateAndStartRefreshInFragment(fm, StatsFollowersFragment.TAG);
}
}
private boolean updateTimeframeAndDateAndStartRefreshInFragment(FragmentManager fm , String fragmentTAG) {
StatsAbstractFragment fragment = (StatsAbstractFragment) fm.findFragmentByTag(fragmentTAG);
if (fragment != null) {
fragment.setDate(mRequestedDate);
fragment.setTimeframe(mCurrentTimeframe);
fragment.refreshStats();
return true;
}
return false;
}
private void startWPComLoginActivity() {
mResultCode = RESULT_CANCELED;
Intent signInIntent = new Intent(this, SignInActivity.class);
signInIntent.putExtra(SignInActivity.EXTRA_JETPACK_SITE_AUTH, mLocalBlogID);
signInIntent.putExtra(
SignInActivity.EXTRA_JETPACK_MESSAGE_AUTH,
getString(R.string.stats_sign_in_jetpack_different_com_account)
);
startActivityForResult(signInIntent, SignInActivity.REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SignInActivity.REQUEST_CODE) {
if (resultCode == RESULT_CANCELED) {
finish();
}
mResultCode = resultCode;
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (resultCode == RESULT_OK && currentBlog != null && !currentBlog.isDotcomFlag()) {
if (currentBlog.getDotComBlogId() == null) {
final Handler handler = new Handler();
// Attempt to get the Jetpack blog ID
XMLRPCClientInterface xmlrpcClient = XMLRPCFactory.instantiate(currentBlog.getUri(), "", "");
Map<String, String> args = ApiHelper.blogOptionsXMLRPCParameters;
Object[] params = {
currentBlog.getRemoteBlogId(), currentBlog.getUsername(), currentBlog.getPassword(), args
};
xmlrpcClient.callAsync(new XMLRPCCallback() {
@Override
public void onSuccess(long id, Object result) {
if (result != null && (result instanceof HashMap)) {
Map<?, ?> blogOptions = (HashMap<?, ?>) result;
ApiHelper.updateBlogOptions(currentBlog, blogOptions);
AnalyticsUtils.refreshMetadata();
AnalyticsUtils.trackWithBlogDetails(AnalyticsTracker.Stat.SIGNED_INTO_JETPACK, currentBlog);
AnalyticsUtils.trackWithBlogDetails(
AnalyticsTracker.Stat.PERFORMED_JETPACK_SIGN_IN_FROM_STATS_SCREEN, currentBlog);
if (isFinishing()) {
return;
}
// We have the blogID now, but we need to re-check if the network connection is available
if (NetworkUtils.checkConnection(StatsActivity.this)) {
handler.post(new Runnable() {
@Override
public void run() {
mSwipeToRefreshHelper.setRefreshing(true);
mRequestedDate = StatsUtils.getCurrentDateTZ(mLocalBlogID);
createFragments(true); // Recreate the fragment and start a refresh of Stats
}
});
}
}
}
@Override
public void onFailure(long id, Exception error) {
AppLog.e(T.STATS,
"Cannot load blog options (wp.getOptions failed) "
+ "and no jetpack_client_id is then available",
error);
handler.post(new Runnable() {
@Override
public void run() {
mSwipeToRefreshHelper.setRefreshing(false);
ToastUtils.showToast(StatsActivity.this,
StatsActivity.this.getString(R.string.error_refresh_stats),
Duration.LONG);
}
});
}
}, Method.GET_OPTIONS, params);
} else {
mRequestedDate = StatsUtils.getCurrentDateTZ(mLocalBlogID);
createFragments(true); // Recreate the fragment and start a refresh of Stats
}
mSwipeToRefreshHelper.setRefreshing(true);
}
}
}
private class VerifyJetpackSettingsCallback implements ApiHelper.GenericCallback {
// AsyncTasks are bound to the Activity that launched it. If the user rotate the device StatsActivity is restarted.
// Use the event bus to fix this issue.
@Override
public void onSuccess() {
EventBus.getDefault().post(new StatsEvents.JetpackSettingsCompleted(false));
}
@Override
public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) {
EventBus.getDefault().post(new StatsEvents.JetpackSettingsCompleted(true));
}
}
private void showJetpackNonConnectedAlert() {
if (isFinishing()) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (currentBlog == null) {
AppLog.e(T.STATS, "The blog with local_blog_id " + mLocalBlogID + " cannot be loaded from the DB.");
Toast.makeText(this, R.string.stats_no_blog, Toast.LENGTH_LONG).show();
finish();
return;
}
if (currentBlog.isAdmin()) {
builder.setMessage(getString(R.string.jetpack_not_connected_message))
.setTitle(getString(R.string.jetpack_not_connected));
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String stringToLoad = currentBlog.getAdminUrl();
String jetpackConnectPageAdminPath = "admin.php?page=jetpack";
stringToLoad = stringToLoad.endsWith("/") ? stringToLoad + jetpackConnectPageAdminPath :
stringToLoad + "/" + jetpackConnectPageAdminPath;
String authURL = WPWebViewActivity.getBlogLoginUrl(currentBlog);
Intent jetpackIntent = new Intent(StatsActivity.this, WPWebViewActivity.class);
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_USER, currentBlog.getUsername());
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_PASSWD, currentBlog.getPassword());
jetpackIntent.putExtra(WPWebViewActivity.URL_TO_LOAD, stringToLoad);
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_URL, authURL);
startActivityForResult(jetpackIntent, REQUEST_JETPACK);
AnalyticsTracker.track(AnalyticsTracker.Stat.STATS_SELECTED_CONNECT_JETPACK);
}
});
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog. Hide Stats.
finish();
}
});
} else {
builder.setMessage(getString(R.string.jetpack_message_not_admin))
.setTitle(getString(R.string.jetpack_not_found));
builder.setPositiveButton(R.string.yes, null);
}
AlertDialog dialog = builder.create();
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// User pressed the back key Hide Stats.
finish();
}
});
dialog.show();
}
private void showJetpackMissingAlert() {
if (isFinishing()) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (currentBlog == null) {
AppLog.e(T.STATS, "The blog with local_blog_id " + mLocalBlogID + " cannot be loaded from the DB.");
Toast.makeText(this, R.string.stats_no_blog, Toast.LENGTH_LONG).show();
finish();
return;
}
if (currentBlog.isAdmin()) {
builder.setMessage(getString(R.string.jetpack_message))
.setTitle(getString(R.string.jetpack_not_found));
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String stringToLoad = currentBlog.getAdminUrl()
+ "plugin-install.php?tab=search&s=jetpack+by+wordpress.com"
+ "&plugin-search-input=Search+Plugins";
String authURL = WPWebViewActivity.getBlogLoginUrl(currentBlog);
Intent jetpackIntent = new Intent(StatsActivity.this, WPWebViewActivity.class);
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_USER, currentBlog.getUsername());
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_PASSWD, currentBlog.getPassword());
jetpackIntent.putExtra(WPWebViewActivity.URL_TO_LOAD, stringToLoad);
jetpackIntent.putExtra(WPWebViewActivity.AUTHENTICATION_URL, authURL);
startActivityForResult(jetpackIntent, REQUEST_JETPACK);
AnalyticsTracker.track(AnalyticsTracker.Stat.STATS_SELECTED_INSTALL_JETPACK);
}
});
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog. Hide Stats.
finish();
}
});
} else {
builder.setMessage(getString(R.string.jetpack_message_not_admin))
.setTitle(getString(R.string.jetpack_not_found));
builder.setPositiveButton(R.string.yes, null);
}
AlertDialog dialog = builder.create();
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// User pressed the back key Hide Stats.
finish();
}
});
dialog.show();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int i = item.getItemId();
if (i == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private void scrollToTop() {
mOuterScrollView.fullScroll(ScrollView.FOCUS_UP);
}
// StatsInsightsTodayFragment calls this when the user taps on a item in Today's Stats
@Override
public void onInsightsTodayClicked(final StatsVisitorsAndViewsFragment.OverviewLabel item) {
mTabToSelectOnGraph = item;
for (int i = 0; i < timeframes.length; i++) {
if (timeframes[i] == StatsTimeframe.DAY) {
mSpinner.setSelection(i);
break;
}
}
}
// StatsVisitorsAndViewsFragment calls this when the user taps on a bar in the graph
@Override
public void onDateChanged(String blogID, StatsTimeframe timeframe, String date) {
if (isFinishing()) {
return;
}
mRequestedDate = date;
updateTimeframeAndDateAndStartRefreshOfFragments(false);
if (NetworkUtils.checkConnection(StatsActivity.this)) {
mSwipeToRefreshHelper.setRefreshing(true);
} else {
mSwipeToRefreshHelper.setRefreshing(false);
}
}
// StatsVisitorsAndViewsFragment calls this when the user taps on the tab bar to change the type of the graph
@Override
public void onOverviewItemChanged(StatsVisitorsAndViewsFragment.OverviewLabel newItem) {
mTabToSelectOnGraph = newItem;
}
private boolean checkCredentials() {
if (!NetworkUtils.isNetworkAvailable(this)) {
AppLog.w(AppLog.T.STATS, "StatsActivity > cannot check credentials since no internet connection available");
return false;
}
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (currentBlog == null) {
AppLog.e(T.STATS, "The blog with local_blog_id " + mLocalBlogID + " cannot be loaded from the DB.");
return false;
}
final String blogId = currentBlog.getDotComBlogId();
// blogId is always available for dotcom blogs. It could be null on Jetpack blogs...
if (blogId != null) {
// for self-hosted sites; launch the user into an activity where they can provide their credentials
if (!currentBlog.isDotcomFlag()
&& !currentBlog.hasValidJetpackCredentials() && mResultCode != RESULT_CANCELED) {
if (AccountHelper.isSignedInWordPressDotCom()) {
// Let's try the global wpcom credentials them first
String username = AccountHelper.getDefaultAccount().getUserName();
currentBlog.setDotcom_username(username);
WordPress.wpDB.saveBlog(currentBlog);
createFragments(true);
} else {
startWPComLoginActivity();
return false;
}
}
} else {
// blogId is null at this point.
if (!currentBlog.isDotcomFlag()) {
// Refresh blog settings/options that includes 'jetpack_client_id' needed here
mSwipeToRefreshHelper.setRefreshing(true);
new ApiHelper.RefreshBlogContentTask(currentBlog,
new VerifyJetpackSettingsCallback()).execute(false);
return false;
} else {
// blodID cannot be null on dotcom blogs.
Toast.makeText(this, R.string.error_refresh_stats, Toast.LENGTH_LONG).show();
AppLog.e(T.STATS, "blogID is null for a wpcom blog!! " + currentBlog.getHomeURL());
finish();
}
}
return true;
}
private void bumpPromoAnaylticsAndShowPromoDialogIfNecessary() {
if (mIsUpdatingStats || mThereWasAnErrorLoadingStats) {
// Do nothing in case of errors or when it's still loading
return;
}
if (!StringUtils.isEmpty(AppPrefs.getStatsWidgetsKeys())) {
// Stats widgets already used!!
return;
}
// Bump analytics that drives the Promo widget when the loading is completed without errors.
AppPrefs.bumpAnalyticsForStatsWidgetPromo();
// Should we display the widget promo?
int counter = AppPrefs.getAnalyticsForStatsWidgetPromo();
if (counter == 3 || counter == 1000 || counter == 10000) {
DialogFragment newFragment = PromoDialog.newInstance(R.drawable.stats_widget_promo_header,
R.string.stats_widget_promo_title, R.string.stats_widget_promo_desc,
R.string.stats_widget_promo_ok_btn_label);
newFragment.show(getFragmentManager(), "promote_widget_dialog");
}
}
@SuppressWarnings("unused")
public void onEventMainThread(StatsEvents.UpdateStatusChanged event) {
if (isFinishing() || !mIsInFront) {
return;
}
mSwipeToRefreshHelper.setRefreshing(event.mUpdating);
mIsUpdatingStats = event.mUpdating;
if (!mIsUpdatingStats && !mThereWasAnErrorLoadingStats) {
// Do not bump promo analytics or show the dialog in case of errors or when it's still loading
bumpPromoAnaylticsAndShowPromoDialogIfNecessary();
}
}
@SuppressWarnings("unused")
public void onEventMainThread(StatsEvents.JetpackSettingsCompleted event) {
if (isFinishing() || !mIsInFront) {
return;
}
mSwipeToRefreshHelper.setRefreshing(false);
if (!event.isError) {
final Blog currentBlog = WordPress.getBlog(mLocalBlogID);
if (currentBlog == null) {
AppLog.e(T.STATS, "The blog with local_blog_id " + mLocalBlogID + " cannot be loaded from the DB.");
Toast.makeText(this, R.string.stats_no_blog, Toast.LENGTH_LONG).show();
finish();
return;
}
if (currentBlog.getDotComBlogId() == null) {
if (TextUtils.isEmpty(currentBlog.getJetpackVersion())) {
// jetpack_version option is available, but not the jetpack_client_id ----> Jetpack available but not connected.
showJetpackNonConnectedAlert();
} else {
// Blog has not returned jetpack_version/jetpack_client_id.
showJetpackMissingAlert();
}
} else {
checkCredentials();
}
} else {
Toast.makeText(StatsActivity.this, R.string.error_refresh_stats, Toast.LENGTH_LONG).show();
finish();
}
}
@SuppressWarnings("unused")
public void onEventMainThread(StatsEvents.SectionUpdateError event) {
// There was an error loading Stats. Don't bump stats for promo widget.
if (isFinishing() || !mIsInFront) {
return;
}
// There was an error loading Stats. Don't bump stats for promo widget.
mThereWasAnErrorLoadingStats = true;
}
@SuppressWarnings("unused")
public void onEventMainThread(StatsEvents.JetpackAuthError event) {
if (isFinishing() || !mIsInFront) {
return;
}
// There was an error loading Stats. Don't bump stats for promo widget.
mThereWasAnErrorLoadingStats = true;
if (event.mLocalBlogId != mLocalBlogID) {
// The user has changed blog
return;
}
mSwipeToRefreshHelper.setRefreshing(false);
startWPComLoginActivity();
}
/*
* make sure the passed timeframe is the one selected in the actionbar
*/
private void selectCurrentTimeframeInActionBar() {
if (isFinishing()) {
return;
}
if (mTimeframeSpinnerAdapter == null || mSpinner == null) {
return;
}
int position = mTimeframeSpinnerAdapter.getIndexOfTimeframe(mCurrentTimeframe);
if (position > -1 && position != mSpinner.getSelectedItemPosition()) {
mSpinner.setSelection(position);
}
}
/*
* adapter used by the timeframe spinner
*/
private class TimeframeSpinnerAdapter extends BaseAdapter {
private final StatsTimeframe[] mTimeframes;
private final LayoutInflater mInflater;
TimeframeSpinnerAdapter(Context context, StatsTimeframe[] timeframeNames) {
super();
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mTimeframes = timeframeNames;
}
@Override
public int getCount() {
return (mTimeframes != null ? mTimeframes.length : 0);
}
@Override
public Object getItem(int position) {
if (position < 0 || position >= getCount())
return "";
return mTimeframes[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.filter_spinner_item, parent, false);
} else {
view = convertView;
}
final TextView text = (TextView) view.findViewById(R.id.text);
StatsTimeframe selectedTimeframe = (StatsTimeframe)getItem(position);
text.setText(selectedTimeframe.getLabel());
return view;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
StatsTimeframe selectedTimeframe = (StatsTimeframe)getItem(position);
final TagViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.toolbar_spinner_dropdown_item, parent, false);
holder = new TagViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (TagViewHolder) convertView.getTag();
}
holder.textView.setText(selectedTimeframe.getLabel());
return convertView;
}
private class TagViewHolder {
private final TextView textView;
TagViewHolder(View view) {
textView = (TextView) view.findViewById(R.id.text);
}
}
public int getIndexOfTimeframe(StatsTimeframe tm) {
int pos = 0;
for (int i = 0; i < mTimeframes.length; i++) {
if (mTimeframes[i] == tm) {
pos = i;
return pos;
}
}
return pos;
}
}
@Override
public void onScrollChanged(NestedScrollViewExt scrollView, int x, int y, int oldx, int oldy) {
// We take the last son in the scrollview
View view = scrollView.getChildAt(scrollView.getChildCount() - 1);
if (view == null) {
return;
}
int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY() + view.getTop()));
// if diff is zero, then the bottom has been reached
if (diff == 0) {
sTrackBottomReachedStats.runIfNotLimited();
}
}
private static final RateLimitedTask sTrackBottomReachedStats = new RateLimitedTask(2) {
protected boolean run() {
AnalyticsTracker.track(AnalyticsTracker.Stat.STATS_SCROLLED_TO_BOTTOM);
return true;
}
};
}