From 1497dadfe0456958a76727c3b1daea53c7b030a4 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli Date: Fri, 29 Apr 2016 16:10:13 +0200 Subject: [PATCH 1/2] Use a Timer, and a TimerTask, to correctly track application `background` state. --- .../java/org/wordpress/android/WordPress.java | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java index 41fc5c1b98c3..f1d92d83429a 100644 --- a/WordPress/src/main/java/org/wordpress/android/WordPress.java +++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java @@ -82,6 +82,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import de.greenrobot.event.EventBus; import io.fabric.sdk.android.Fabric; @@ -698,8 +700,11 @@ private class ApplicationLifecycleMonitor implements Application.ActivityLifecyc private final int DEFAULT_TIMEOUT = 2 * 60; // 2 minutes private Date mLastPingDate; private Date mApplicationOpenedDate; - boolean mIsInBackground = true; boolean mFirstActivityResumed = true; + private Timer mActivityTransitionTimer; + private TimerTask mActivityTransitionTimerTask; + private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000; + boolean mIsInBackground = true; @Override public void onConfigurationChanged(final Configuration newConfig) { @@ -713,25 +718,6 @@ public void onLowMemory() { @Override public void onTrimMemory(final int level) { - if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { - // We're in the Background - mIsInBackground = true; - String lastActivityString = AppPrefs.getLastActivityStr(); - ActivityId lastActivity = ActivityId.getActivityIdFromName(lastActivityString); - Map properties = new HashMap(); - properties.put("last_visible_screen", lastActivity.toString()); - if (mApplicationOpenedDate != null) { - Date now = new Date(); - properties.put("time_in_app", DateTimeUtils.secondsBetween(now, mApplicationOpenedDate)); - mApplicationOpenedDate = null; - } - AnalyticsTracker.track(AnalyticsTracker.Stat.APPLICATION_CLOSED, properties); - AnalyticsTracker.endSession(false); - onAppGoesToBackground(); - } else { - mIsInBackground = false; - } - boolean evictBitmaps = false; switch (level) { case TRIM_MEMORY_COMPLETE: @@ -776,9 +762,42 @@ private void updatePushNotificationTokenIfNotLimited() { } } - public void onAppGoesToBackground() { - AppLog.i(T.UTILS, "App goes to background"); - ConnectionChangeReceiver.setEnabled(WordPress.this, false); + private void startActivityTransitionTimer() { + this.mActivityTransitionTimer = new Timer(); + this.mActivityTransitionTimerTask = new TimerTask() { + public void run() { + AppLog.i(T.UTILS, "App goes to background"); + // We're in the Background + mIsInBackground = true; + String lastActivityString = AppPrefs.getLastActivityStr(); + ActivityId lastActivity = ActivityId.getActivityIdFromName(lastActivityString); + Map properties = new HashMap(); + properties.put("last_visible_screen", lastActivity.toString()); + if (mApplicationOpenedDate != null) { + Date now = new Date(); + properties.put("time_in_app", DateTimeUtils.secondsBetween(now, mApplicationOpenedDate)); + mApplicationOpenedDate = null; + } + AnalyticsTracker.track(AnalyticsTracker.Stat.APPLICATION_CLOSED, properties); + AnalyticsTracker.endSession(false); + ConnectionChangeReceiver.setEnabled(WordPress.this, false); + } + }; + + this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask, + MAX_ACTIVITY_TRANSITION_TIME_MS); + } + + private void stopActivityTransitionTimer() { + if (this.mActivityTransitionTimerTask != null) { + this.mActivityTransitionTimerTask.cancel(); + } + + if (this.mActivityTransitionTimer != null) { + this.mActivityTransitionTimer.cancel(); + } + + mIsInBackground = false; } /** @@ -786,7 +805,7 @@ public void onAppGoesToBackground() { * 1. the app starts (but it's not opened by a service or a broadcast receiver, i.e. an activity is resumed) * 2. the app was in background and is now foreground */ - public void onAppComesFromBackground() { + private void onAppComesFromBackground() { AppLog.i(T.UTILS, "App comes from background"); ConnectionChangeReceiver.setEnabled(WordPress.this, true); AnalyticsUtils.refreshMetadata(); @@ -811,6 +830,8 @@ public void onActivityResumed(Activity activity) { // was in background before onAppComesFromBackground(); } + stopActivityTransitionTimer(); + mIsInBackground = false; if (mFirstActivityResumed) { deferredInit(activity); @@ -829,6 +850,7 @@ public void onActivityDestroyed(Activity arg0) { @Override public void onActivityPaused(Activity arg0) { mLastPingDate = new Date(); + startActivityTransitionTimer(); } @Override From ca9d45ebada86b8518bf97412a256b9ab07169e0 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli Date: Mon, 16 May 2016 16:07:02 +0200 Subject: [PATCH 2/2] Added some comments that describe how we're detecting the "onBackground" state. --- .../main/java/org/wordpress/android/WordPress.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/WordPress.java b/WordPress/src/main/java/org/wordpress/android/WordPress.java index f1d92d83429a..1e4a2149df6e 100644 --- a/WordPress/src/main/java/org/wordpress/android/WordPress.java +++ b/WordPress/src/main/java/org/wordpress/android/WordPress.java @@ -762,6 +762,20 @@ private void updatePushNotificationTokenIfNotLimited() { } } + /** + * The two methods below (startActivityTransitionTimer and stopActivityTransitionTimer) + * are used to track when the app goes to background. + * + * Our implementation uses `onActivityPaused` and `onActivityResumed` of ApplicationLifecycleMonitor + * to start and stop the timer that detects when the app goes to background. + * + * So when the user is simply navigating between the activities, the onActivityPaused() calls `startActivityTransitionTimer` + * and starts the timer, but almost immediately the new activity being entered, the ApplicationLifecycleMonitor cancels the timer + * in its onActivityResumed method, that in order calls `stopActivityTransitionTimer`. + * And so mIsInBackground would be false. + * + * In the case the app is sent to background, the TimerTask is instead executed, and the code that handles all the background logic is run. + */ private void startActivityTransitionTimer() { this.mActivityTransitionTimer = new Timer(); this.mActivityTransitionTimerTask = new TimerTask() {