Merge "Delay showing the loading progress spinner in Managed App." into oc-dr1-dev
am: 153e2b89b9
Change-Id: I3eaa11054beabf83765bf548a5dca6337817a546
This commit is contained in:
@@ -943,6 +943,8 @@ public final class Utils extends com.android.settingslib.Utils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move this out of Utils to a mixin or a controller or a helper class.
|
||||||
|
@Deprecated
|
||||||
public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
|
public static void handleLoadingContainer(View loading, View doneLoading, boolean done,
|
||||||
boolean animate) {
|
boolean animate) {
|
||||||
setViewShown(loading, !done, animate);
|
setViewShown(loading, !done, animate);
|
||||||
|
@@ -346,7 +346,6 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
|
|
||||||
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
|
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
|
||||||
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
|
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
|
||||||
mLoadingContainer.setVisibility(View.VISIBLE);
|
|
||||||
mListContainer = mRootView.findViewById(R.id.list_container);
|
mListContainer = mRootView.findViewById(R.id.list_container);
|
||||||
if (mListContainer != null) {
|
if (mListContainer != null) {
|
||||||
// Create adapter and list view here
|
// Create adapter and list view here
|
||||||
@@ -395,7 +394,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
return mRootView;
|
return mRootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createHeader() {
|
@VisibleForTesting
|
||||||
|
void createHeader() {
|
||||||
Activity activity = getActivity();
|
Activity activity = getActivity();
|
||||||
FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
|
FrameLayout pinnedHeader = (FrameLayout) mRootView.findViewById(R.id.pinned_header);
|
||||||
mSpinnerHeader = activity.getLayoutInflater()
|
mSpinnerHeader = activity.getLayoutInflater()
|
||||||
@@ -834,6 +834,10 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
static class ApplicationsAdapter extends BaseAdapter implements Filterable,
|
static class ApplicationsAdapter extends BaseAdapter implements Filterable,
|
||||||
ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
|
ApplicationsState.Callbacks, AppStateBaseBridge.Callback,
|
||||||
AbsListView.RecyclerListener, SectionIndexer {
|
AbsListView.RecyclerListener, SectionIndexer {
|
||||||
|
|
||||||
|
// how long to wait for app list to populate without showing the loading container
|
||||||
|
private static final long DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS = 100L;
|
||||||
|
|
||||||
private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
|
private static final SectionInfo[] EMPTY_SECTIONS = new SectionInfo[0];
|
||||||
|
|
||||||
private final ApplicationsState mState;
|
private final ApplicationsState mState;
|
||||||
@@ -889,6 +893,13 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private Runnable mShowLoadingContainerRunnable = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
|
||||||
|
mManageApplications.mListContainer, false /* done */, false /* animate */);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
|
public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
|
||||||
int filterMode) {
|
int filterMode) {
|
||||||
mState = state;
|
mState = state;
|
||||||
@@ -1097,6 +1108,9 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
|
|
||||||
if (mSession.getAllApps().size() != 0
|
if (mSession.getAllApps().size() != 0
|
||||||
&& mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
|
&& mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
|
||||||
|
// Cancel any pending task to show the loading animation and show the list of
|
||||||
|
// apps directly.
|
||||||
|
mFgHandler.removeCallbacks(mShowLoadingContainerRunnable);
|
||||||
Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
|
Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
|
||||||
mManageApplications.mListContainer, true, true);
|
mManageApplications.mListContainer, true, true);
|
||||||
}
|
}
|
||||||
@@ -1148,10 +1162,16 @@ public class ManageApplications extends InstrumentedPreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoading() {
|
@VisibleForTesting
|
||||||
Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
|
void updateLoading() {
|
||||||
mManageApplications.mListContainer,
|
final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0;
|
||||||
mHasReceivedLoadEntries && mSession.getAllApps().size() != 0, false);
|
if (appLoaded) {
|
||||||
|
Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
|
||||||
|
mManageApplications.mListContainer, true /* done */, false /* animate */);
|
||||||
|
} else {
|
||||||
|
mFgHandler.postDelayed(
|
||||||
|
mShowLoadingContainerRunnable, DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
|
ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
|
||||||
|
@@ -17,29 +17,38 @@
|
|||||||
package com.android.settings.applications;
|
package com.android.settings.applications;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Settings;
|
import com.android.settings.Settings;
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
|
import com.android.settings.testutils.shadow.SettingsShadowResources.SettingsShadowTheme;
|
||||||
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
|
||||||
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
import com.android.settings.testutils.shadow.ShadowEventLogWriter;
|
||||||
import com.android.settingslib.applications.ApplicationsState;
|
import com.android.settingslib.applications.ApplicationsState;
|
||||||
|
import com.android.settingslib.applications.ApplicationsState.Callbacks;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.fakes.RoboMenuItem;
|
import org.robolectric.fakes.RoboMenuItem;
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
@@ -47,7 +56,12 @@ import org.robolectric.util.ReflectionHelpers;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
import static org.mockito.Matchers.anyLong;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
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.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@@ -132,6 +146,107 @@ public class ManageApplicationsTest {
|
|||||||
assertThat(mMenu.findItem(R.id.reset_app_preferences).isVisible()).isFalse();
|
assertThat(mMenu.findItem(R.id.reset_app_preferences).isVisible()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreateView_shouldNotShowLoadingContainer() {
|
||||||
|
final ManageApplications fragment = spy(new ManageApplications());
|
||||||
|
ReflectionHelpers.setField(fragment, "mResetAppsHelper",
|
||||||
|
mock(ResetAppsHelper.class));
|
||||||
|
doNothing().when(fragment).createHeader();
|
||||||
|
|
||||||
|
final LayoutInflater layoutInflater = mock(LayoutInflater.class);
|
||||||
|
final View view = mock(View.class);
|
||||||
|
final View loadingContainer = mock(View.class);
|
||||||
|
when(layoutInflater.inflate(anyInt(), eq(null))).thenReturn(view);
|
||||||
|
when(view.findViewById(R.id.loading_container)).thenReturn(loadingContainer);
|
||||||
|
|
||||||
|
fragment.onCreateView(layoutInflater, mock(ViewGroup.class), null);
|
||||||
|
|
||||||
|
verify(loadingContainer, never()).setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateLoading_appLoaded_shouldNotDelayCallToHandleLoadingContainer() {
|
||||||
|
final ManageApplications fragment = mock(ManageApplications.class);
|
||||||
|
ReflectionHelpers.setField(fragment, "mLoadingContainer", mock(View.class));
|
||||||
|
ReflectionHelpers.setField(fragment, "mListContainer", mock(View.class));
|
||||||
|
when(fragment.getActivity()).thenReturn(mock(Activity.class));
|
||||||
|
final Runnable showLoadingContainerRunnable = mock(Runnable.class);
|
||||||
|
final Handler handler = mock(Handler.class);
|
||||||
|
final ManageApplications.ApplicationsAdapter adapter =
|
||||||
|
spy(new ManageApplications.ApplicationsAdapter(mState, fragment, 0));
|
||||||
|
ReflectionHelpers.setField(adapter, "mShowLoadingContainerRunnable",
|
||||||
|
showLoadingContainerRunnable);
|
||||||
|
ReflectionHelpers.setField(adapter, "mFgHandler", handler);
|
||||||
|
|
||||||
|
// app loading completed
|
||||||
|
ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", true);
|
||||||
|
final ArrayList<ApplicationsState.AppEntry> appList = new ArrayList<>();
|
||||||
|
appList.add(mock(ApplicationsState.AppEntry.class));
|
||||||
|
when(mSession.getAllApps()).thenReturn(appList);
|
||||||
|
|
||||||
|
adapter.updateLoading();
|
||||||
|
|
||||||
|
verify(handler, never()).postDelayed(eq(showLoadingContainerRunnable), anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateLoading_appNotLoaded_shouldDelayCallToHandleLoadingContainer() {
|
||||||
|
final ManageApplications fragment = mock(ManageApplications.class);
|
||||||
|
ReflectionHelpers.setField(fragment, "mLoadingContainer", mock(View.class));
|
||||||
|
ReflectionHelpers.setField(fragment, "mListContainer", mock(View.class));
|
||||||
|
when(fragment.getActivity()).thenReturn(mock(Activity.class));
|
||||||
|
final Runnable showLoadingContainerRunnable = mock(Runnable.class);
|
||||||
|
final Handler handler = mock(Handler.class);
|
||||||
|
final ManageApplications.ApplicationsAdapter adapter =
|
||||||
|
spy(new ManageApplications.ApplicationsAdapter(mState, fragment, 0));
|
||||||
|
ReflectionHelpers.setField(adapter, "mShowLoadingContainerRunnable",
|
||||||
|
showLoadingContainerRunnable);
|
||||||
|
ReflectionHelpers.setField(adapter, "mFgHandler", handler);
|
||||||
|
|
||||||
|
// app loading not yet completed
|
||||||
|
ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", false);
|
||||||
|
|
||||||
|
adapter.updateLoading();
|
||||||
|
|
||||||
|
verify(handler).postDelayed(eq(showLoadingContainerRunnable), anyLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onRebuildComplete_shouldCancelDelayedRunnable() {
|
||||||
|
final Context context = RuntimeEnvironment.application;
|
||||||
|
final ManageApplications fragment = mock(ManageApplications.class);
|
||||||
|
final View loadingContainer = mock(View.class);
|
||||||
|
when(loadingContainer.getContext()).thenReturn(context);
|
||||||
|
final View listContainer = mock(View.class);
|
||||||
|
when(listContainer.getVisibility()).thenReturn(View.INVISIBLE);
|
||||||
|
when(listContainer.getContext()).thenReturn(context);
|
||||||
|
ReflectionHelpers.setField(fragment, "mLoadingContainer", loadingContainer);
|
||||||
|
ReflectionHelpers.setField(fragment, "mListContainer", listContainer);
|
||||||
|
when(fragment.getActivity()).thenReturn(mock(Activity.class));
|
||||||
|
final Runnable showLoadingContainerRunnable = mock(Runnable.class);
|
||||||
|
final Handler handler = mock(Handler.class);
|
||||||
|
final ManageApplications.ApplicationsAdapter adapter =
|
||||||
|
spy(new ManageApplications.ApplicationsAdapter(mState, fragment, 0));
|
||||||
|
ReflectionHelpers.setField(adapter, "mShowLoadingContainerRunnable",
|
||||||
|
showLoadingContainerRunnable);
|
||||||
|
ReflectionHelpers.setField(adapter, "mFgHandler", handler);
|
||||||
|
ReflectionHelpers.setField(adapter, "mFilterMode", -1);
|
||||||
|
|
||||||
|
// app loading not yet completed
|
||||||
|
ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", false);
|
||||||
|
adapter.updateLoading();
|
||||||
|
|
||||||
|
// app loading completed
|
||||||
|
ReflectionHelpers.setField(adapter, "mHasReceivedLoadEntries", true);
|
||||||
|
final ArrayList<ApplicationsState.AppEntry> appList = new ArrayList<>();
|
||||||
|
appList.add(mock(ApplicationsState.AppEntry.class));
|
||||||
|
when(mSession.getAllApps()).thenReturn(appList);
|
||||||
|
|
||||||
|
adapter.onRebuildComplete(null);
|
||||||
|
|
||||||
|
verify(handler).removeCallbacks(showLoadingContainerRunnable);
|
||||||
|
}
|
||||||
|
|
||||||
private void setUpOptionMenus() {
|
private void setUpOptionMenus() {
|
||||||
when(mMenu.findItem(anyInt())).thenAnswer(invocation -> {
|
when(mMenu.findItem(anyInt())).thenAnswer(invocation -> {
|
||||||
final Object[] args = invocation.getArguments();
|
final Object[] args = invocation.getArguments();
|
||||||
|
Reference in New Issue
Block a user