Fix the endless panel loading

Re-launching volume panel continuously will trigger an endless panel
loading, show a transparent unfinished UI, and then block the user's
screen.

Root cause:
When the activity receives a new intent from user's clicking, it will
call PanelFragment#createPanelContent to update the current fragment.
The method triggers an animation and then loads the panel content. If
multiple invocations run concurrently before the animation or the
loading finish, the loader's countdown latch will be increased
abnormally and lead to the endless loading.

Solution:
1. Since the invocations are in UI thread, simply add a flag to avoid
reentrance when the panel is animating or loading.
2. Filter out the same panel's creation request when the panel is still
visible.
3. Do not force a panel's recreation when it's under construction.

Bug: 143889510
Bug: 160491854
Test: robotest, manual
Change-Id: I821faedeb62354929f3af9804cbbe44ee5bb8a53
This commit is contained in:
Jason Chiu
2020-08-06 16:05:33 +08:00
parent 0e3c359888
commit 6a8d2c5e55
3 changed files with 100 additions and 10 deletions

View File

@@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,6 +37,9 @@ import android.os.Build;
import android.view.Window;
import android.view.WindowManager;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.HideNonSystemOverlayMixin;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -43,6 +47,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@@ -56,6 +61,10 @@ public class SettingsPanelActivityTest {
private FakeSettingsPanelActivity mSettingsPanelActivity;
private PanelFeatureProvider mPanelFeatureProvider;
private FakePanelContent mFakePanelContent;
@Mock
private PanelFragment mPanelFragment;
@Mock
private FragmentManager mFragmentManager;
@Before
public void setUp() {
@@ -67,6 +76,10 @@ public class SettingsPanelActivityTest {
mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
mFakePanelContent = new FakePanelContent();
doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any());
mSettingsPanelActivity.mPanelFragment = mPanelFragment;
when(mFragmentManager.findFragmentById(R.id.main_content)).thenReturn(mPanelFragment);
when(mSettingsPanelActivity.getSupportFragmentManager()).thenReturn(mFragmentManager);
}
@Test
@@ -141,11 +154,62 @@ public class SettingsPanelActivityTest {
& SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
}
@Test
public void onStop_panelIsNotCreating_shouldForceUpdate() {
mSettingsPanelActivity.mForceCreation = false;
when(mPanelFragment.isPanelCreating()).thenReturn(false);
mSettingsPanelActivity.mPanelFragment = mPanelFragment;
mSettingsPanelActivity.onStop();
assertThat(mSettingsPanelActivity.mForceCreation).isTrue();
}
@Test
public void onStop_panelIsCreating_shouldNotForceUpdate() {
mSettingsPanelActivity.mForceCreation = false;
when(mPanelFragment.isPanelCreating()).thenReturn(true);
mSettingsPanelActivity.mPanelFragment = mPanelFragment;
mSettingsPanelActivity.onStop();
assertThat(mSettingsPanelActivity.mForceCreation).isFalse();
}
@Test
public void onConfigurationChanged_shouldForceUpdate() {
mSettingsPanelActivity.mForceCreation = false;
mSettingsPanelActivity.onConfigurationChanged(new Configuration());
assertThat(mSettingsPanelActivity.mForceCreation).isTrue();
}
@Test
public void onNewIntent_panelIsNotCreating_shouldUpdatePanel() {
when(mPanelFragment.isPanelCreating()).thenReturn(false);
mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
verify(mPanelFragment).updatePanelWithAnimation();
}
@Test
public void onNewIntent_panelIsCreating_shouldNotUpdatePanel() {
when(mPanelFragment.isPanelCreating()).thenReturn(true);
mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
verify(mPanelFragment, never()).updatePanelWithAnimation();
}
@Test
public void onNewIntent_panelIsShowingTheSameAction_shouldNotUpdatePanel() {
when(mPanelFragment.isPanelCreating()).thenReturn(false);
when(mPanelFragment.getArguments()).thenReturn(mSettingsPanelActivity.mBundle);
mSettingsPanelActivity.onNewIntent(mSettingsPanelActivity.getIntent());
verify(mPanelFragment, never()).updatePanelWithAnimation();
}
}