Skip to content

Commit

Permalink
Merge pull request #1412 from microsoft/develop
Browse files Browse the repository at this point in the history
Version 3.2.1
  • Loading branch information
zhangcc01 authored Apr 23, 2020
2 parents 4bc6c5a + 49714d2 commit 46937a9
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 31 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# App Center SDK for Android Change Log

## Version 3.2.1

### App Center Distribute

* **[Fix]** Fix checking updates when application switches from background to foreground if the SDK was started after `onCreate` callback.

___

## Version 3.2.0

### App Center Crashes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import static java.lang.Math.max;

/**
* Listens to the whole application (if the application is a single process) lifecycle events.
* It allows catching of a foregrounded and a backgrounded state of the whole application.
Expand Down Expand Up @@ -139,7 +141,18 @@ public void onActivityResumed(@NonNull Activity activity) {

@Override
public void onActivityPaused(@NonNull Activity activity) {
mResumedCounter--;

/*
* If the SDK starts from onStart or onResume, the first onActivityStarted/onActivityResumed isn't called.
* This breaks the flags logic and we need to restore their state.
*/
if (mStartedCounter == 0) {
mStopSent = false;
}
if (mResumedCounter == 0) {
mPauseSent = false;
}
mResumedCounter = max(mResumedCounter - 1, 0);
if (mResumedCounter == 0) {

/*
Expand All @@ -154,7 +167,7 @@ public void onActivityPaused(@NonNull Activity activity) {

@Override
public void onActivityStopped(@NonNull Activity activity) {
mStartedCounter--;
mStartedCounter = max(mStartedCounter - 1, 0);
dispatchStopIfNeeded();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,12 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;

import java.util.Timer;
import java.util.TimerTask;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
Expand All @@ -45,14 +38,6 @@ public class ApplicationLifecycleListenerTest {
@Before
public void setUp() {
mApplicationLifecycleListener = new ApplicationLifecycleListener(mHandlerMock);
doAnswer(new Answer<Void>() {

@Override
public Void answer(InvocationOnMock invocation) {
((Runnable) invocation.getArguments()[0]).run();
return null;
}
}).when(mHandlerMock).postDelayed(any(Runnable.class), anyLong());
}

@Test
Expand All @@ -62,9 +47,6 @@ public void activityResumesBeforeDelayedCallback() {
ApplicationLifecycleListener.ApplicationLifecycleCallbacks callbacks = mock(ApplicationLifecycleListener.ApplicationLifecycleCallbacks.class);
mApplicationLifecycleListener.registerApplicationLifecycleCallbacks(callbacks);

/* Do nothing and trigger manually captured runnable later to ensure that application resumes first. */
reset(mHandlerMock);

/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));
Expand All @@ -86,8 +68,10 @@ public void activityResumesBeforeDelayedCallback() {
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock).removeCallbacks(any(Runnable.class));

/* Verify that enter background callback won't call after the delay. */
/* Runnable from onActivityPaused is called after the timeout. */
postDelayed.getValue().run();

/* Verify that enter background callback won't call after the delay. */
verify(callbacks, never()).onApplicationEnterBackground();
}

Expand Down Expand Up @@ -168,14 +152,20 @@ public void expectedLifecycle() {
/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));
mApplicationLifecycleListener.onActivitySaveInstanceState(mActivityMock, mockBundle);

/* Call onActivityPaused. */
mApplicationLifecycleListener.onActivityPaused(mActivityMock);
verify(mHandlerMock).postDelayed(any(Runnable.class), anyLong());
ArgumentCaptor<Runnable> postDelayedRunnable = ArgumentCaptor.forClass(Runnable.class);
verify(mHandlerMock).postDelayed(postDelayedRunnable.capture(), anyLong());

/* Call onActivityStopped. */
mApplicationLifecycleListener.onActivityStopped(mActivityMock);
mApplicationLifecycleListener.onActivitySaveInstanceState(mActivityMock, mockBundle);

/* Runnable from onActivityPaused is called after the timeout. */
postDelayedRunnable.getValue().run();

/* Verify background callbacks called once. */
verify(callbacks1).onApplicationEnterBackground();
verify(callbacks2).onApplicationEnterBackground();

Expand All @@ -184,6 +174,16 @@ public void expectedLifecycle() {

/* Call onActivityStarted. */
mApplicationLifecycleListener.onActivityStarted(mActivityMock);

/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));

/* Verify background callbacks still were called once. */
verify(callbacks1).onApplicationEnterBackground();
verify(callbacks2).onApplicationEnterBackground();

/* Verify foreground callbacks were called twice. */
verify(callbacks1, times(2)).onApplicationEnterForeground();
verify(callbacks2, times(2)).onApplicationEnterForeground();
}
Expand All @@ -194,10 +194,6 @@ public void activityTransitionTest() {
/* Prepare data. */
Activity anotherActivityMock = mock(Activity.class);
Bundle mockBundle = mock(Bundle.class);

/* Do nothing and trigger manually captured runnable later to ensure that application resumes first. */
reset(mHandlerMock);

ApplicationLifecycleListener.ApplicationLifecycleCallbacks callbacks1 = mock(ApplicationLifecycleListener.ApplicationLifecycleCallbacks.class);
ApplicationLifecycleListener.ApplicationLifecycleCallbacks callbacks2 = mock(ApplicationLifecycleListener.ApplicationLifecycleCallbacks.class);
mApplicationLifecycleListener.registerApplicationLifecycleCallbacks(callbacks1);
Expand All @@ -224,15 +220,90 @@ public void activityTransitionTest() {
mApplicationLifecycleListener.onActivityStarted(anotherActivityMock);
mApplicationLifecycleListener.onActivityResumed(anotherActivityMock);

/* Verify the runnable was removed in onActivityResumed. */
verify(mHandlerMock).removeCallbacks(any(Runnable.class));

/* Call onActivityStopped. */
mApplicationLifecycleListener.onActivityStopped(mActivityMock);

/* Verify that delayed callback doesn't trigger enter background callbacks. */
postDelayed.getValue().run();
verify(callbacks1, never()).onApplicationEnterBackground();
verify(callbacks2, never()).onApplicationEnterBackground();

/* Verify the callback was not called if we transition to another activity inside the app. */
verifyNoMoreInteractions(callbacks1, callbacks2);
}

@Test
public void skipCallOnStartOpenFoldOpen() {

/* Prepare data. */
ApplicationLifecycleListener.ApplicationLifecycleCallbacks callbacks = mock(ApplicationLifecycleListener.ApplicationLifecycleCallbacks.class);
mApplicationLifecycleListener.registerApplicationLifecycleCallbacks(callbacks);

/* Skip call onActivityStarted. */

/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));

/* Call onActivityPaused. */
mApplicationLifecycleListener.onActivityPaused(mActivityMock);
ArgumentCaptor<Runnable> postDelayedRunnable = ArgumentCaptor.forClass(Runnable.class);
verify(mHandlerMock).postDelayed(postDelayedRunnable.capture(), anyLong());

/* Call onActivityStopped. */
mApplicationLifecycleListener.onActivityStopped(mActivityMock);

/* Verify the callback was not called if we transition to another activity inside the app. */
verifyNoMoreInteractions(callbacks1, callbacks2);
/* Runnable from onActivityPaused is called after the timeout. */
postDelayedRunnable.getValue().run();

/* Verify onApplicationEnterBackground was called only once. */
verify(callbacks).onApplicationEnterBackground();

/* Call onActivityStarted. */
mApplicationLifecycleListener.onActivityStarted(mActivityMock);
verify(callbacks).onApplicationEnterForeground();

/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));
}

@Test
public void skipCallOnResumeOpenFoldOpen() {

/* Prepare data. */
ApplicationLifecycleListener.ApplicationLifecycleCallbacks callbacks = mock(ApplicationLifecycleListener.ApplicationLifecycleCallbacks.class);
mApplicationLifecycleListener.registerApplicationLifecycleCallbacks(callbacks);

/* Skip call onActivityStarted and onActivityResume. */

/* Call onActivityPaused. */
mApplicationLifecycleListener.onActivityPaused(mActivityMock);
ArgumentCaptor<Runnable> postDelayedRunnable = ArgumentCaptor.forClass(Runnable.class);
verify(mHandlerMock).postDelayed(postDelayedRunnable.capture(), anyLong());

/* Call onActivityStopped. */
mApplicationLifecycleListener.onActivityStopped(mActivityMock);

/* Runnable for onActivityPaused is called after the timeout. */
postDelayedRunnable.getValue().run();

/* Verify onApplicationEnterBackground called. */
verify(callbacks).onApplicationEnterBackground();

/* Call onActivityStarted. */
mApplicationLifecycleListener.onActivityStarted(mActivityMock);
verify(callbacks).onApplicationEnterForeground();

/* Call onActivityResumed. */
mApplicationLifecycleListener.onActivityResumed(mActivityMock);
verify(mHandlerMock, never()).removeCallbacks(any(Runnable.class));

/* Verify the callbacks were called only once. */
verify(callbacks).onApplicationEnterBackground();
verify(callbacks).onApplicationEnterForeground();
}
}
4 changes: 2 additions & 2 deletions versions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
// Version constants

ext {
versionCode = 55
versionName = '3.2.0'
versionCode = 56
versionName = '3.2.1'
minSdkVersion = 16
targetSdkVersion = 29
compileSdkVersion = 29
Expand Down

0 comments on commit 46937a9

Please sign in to comment.