diff --git a/res/layout/manage_applications_apps.xml b/res/layout/manage_applications_apps.xml index 055e4b453e3..d814164647a 100644 --- a/res/layout/manage_applications_apps.xml +++ b/res/layout/manage_applications_apps.xml @@ -14,7 +14,7 @@ limitations under the License. --> - + android:elevation="2dp"/> - + android:layout_height="match_parent" + android:visibility="gone"> - + - + - + + + + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 33fe1f572ac..908b6401aed 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -149,6 +149,8 @@ 182dp 32dp 24dp + + 80dp 88dip diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java index b1689d5c591..4d13241126f 100644 --- a/src/com/android/settings/applications/RunningServices.java +++ b/src/com/android/settings/applications/RunningServices.java @@ -72,11 +72,7 @@ public class RunningServices extends SettingsPreferenceFragment { public void onResume() { super.onResume(); boolean haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail); - if (haveData) { - mLoadingViewController.showContent(false /* animate */); - } else { - mLoadingViewController.showLoadingView(); - } + mLoadingViewController.handleLoadingContainer(haveData /* done */, false /* animate */); } @Override diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 43e929b8054..6d675240b64 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -208,6 +208,7 @@ public class ManageApplications extends InstrumentedFragment private ApplicationsAdapter mApplications; private View mLoadingContainer; + private View mListContainer; private SearchView mSearchView; // Size resource used for packages whose size computation failed for some reason @@ -401,21 +402,25 @@ public class ManageApplications extends InstrumentedFragment mRootView = inflater.inflate(R.layout.manage_applications_apps, null); mLoadingContainer = mRootView.findViewById(R.id.loading_container); - mEmptyView = mRootView.findViewById(android.R.id.empty); - mRecyclerView = mRootView.findViewById(R.id.apps_list); + mListContainer = mRootView.findViewById(R.id.list_container); + if (mListContainer != null) { + // Create adapter and list view here + mEmptyView = mListContainer.findViewById(android.R.id.empty); - mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, - savedInstanceState); - if (savedInstanceState != null) { - mApplications.mHasReceivedLoadEntries = - savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); - mApplications.mHasReceivedBridgeCallback = - savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false); + mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter, + savedInstanceState); + if (savedInstanceState != null) { + mApplications.mHasReceivedLoadEntries = + savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false); + mApplications.mHasReceivedBridgeCallback = + savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false); + } + mRecyclerView = mListContainer.findViewById(R.id.apps_list); + mRecyclerView.setItemAnimator(null); + mRecyclerView.setLayoutManager(new LinearLayoutManager( + getContext(), RecyclerView.VERTICAL, false /* reverseLayout */)); + mRecyclerView.setAdapter(mApplications); } - mRecyclerView.setItemAnimator(null); - mRecyclerView.setLayoutManager(new LinearLayoutManager( - getContext(), RecyclerView.VERTICAL, false /* reverseLayout */)); - mRecyclerView.setAdapter(mApplications); // We have to do this now because PreferenceFrameLayout looks at it // only when the view is added. @@ -980,8 +985,16 @@ public class ManageApplications extends InstrumentedFragment // overlapped by floating filter. if (hasFilter) { mManageApplications.mSpinnerHeader.setVisibility(View.VISIBLE); + mManageApplications.mRecyclerView.setPadding(0 /* left */, + mContext.getResources().getDimensionPixelSize( + R.dimen.app_bar_height) /* top */, + 0 /* right */, + 0 /* bottom */); } else { mManageApplications.mSpinnerHeader.setVisibility(View.GONE); + mManageApplications.mRecyclerView.setPadding(0 /* left */, 0 /* top */, + 0 /* right */, + 0 /* bottom */); } } } @@ -1031,8 +1044,7 @@ public class ManageApplications extends InstrumentedFragment mManageApplications = manageApplications; mLoadingViewController = new LoadingViewController( mManageApplications.mLoadingContainer, - mManageApplications.mRecyclerView, - mManageApplications.mEmptyView + mManageApplications.mListContainer ); mContext = manageApplications.getActivity(); mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); @@ -1291,9 +1303,11 @@ public class ManageApplications extends InstrumentedFragment mOriginalEntries = entries; notifyDataSetChanged(); if (getItemCount() == 0) { - mLoadingViewController.showEmpty(false /* animate */); + mManageApplications.mRecyclerView.setVisibility(View.GONE); + mManageApplications.mEmptyView.setVisibility(View.VISIBLE); } else { - mLoadingViewController.showContent(false /* animate */); + mManageApplications.mEmptyView.setVisibility(View.GONE); + mManageApplications.mRecyclerView.setVisibility(View.VISIBLE); if (mManageApplications.mSearchView != null && mManageApplications.mSearchView.isVisibleToUser()) { @@ -1310,6 +1324,10 @@ public class ManageApplications extends InstrumentedFragment mLastIndex = -1; } + if (mSession.getAllApps().size() != 0 + && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) { + mLoadingViewController.showContent(true /* animate */); + } if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { // No enabled or disabled filters for usage access. return; diff --git a/src/com/android/settings/widget/LoadingViewController.java b/src/com/android/settings/widget/LoadingViewController.java index 66eebf387ba..294e55e7ea8 100644 --- a/src/com/android/settings/widget/LoadingViewController.java +++ b/src/com/android/settings/widget/LoadingViewController.java @@ -22,66 +22,34 @@ import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; -import androidx.annotation.Nullable; - /** - * A helper class that manages show/hide loading spinner, content view and empty view (optional). + * A helper class that manages show/hide loading spinner. */ public class LoadingViewController { private static final long DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS = 100L; - private final Handler mFgHandler; - private final View mLoadingView; - private final View mContentView; - private final View mEmptyView; + public final Handler mFgHandler; + public final View mLoadingView; + public final View mContentView; public LoadingViewController(View loadingView, View contentView) { - this(loadingView, contentView, null /* emptyView*/); - } - - public LoadingViewController(View loadingView, View contentView, @Nullable View emptyView) { mLoadingView = loadingView; mContentView = contentView; - mEmptyView = emptyView; mFgHandler = new Handler(Looper.getMainLooper()); } private Runnable mShowLoadingContainerRunnable = new Runnable() { public void run() { - showLoadingView(); + handleLoadingContainer(false /* done */, false /* animate */); } }; - /** - * Shows content view and hides loading view & empty view. - */ public void showContent(boolean animate) { // Cancel any pending task to show the loading animation and show the list of // apps directly. mFgHandler.removeCallbacks(mShowLoadingContainerRunnable); - handleLoadingContainer(true /* showContent */, false /* showEmpty*/, animate); - } - - /** - * Shows empty view and hides loading view & content view. - */ - public void showEmpty(boolean animate) { - if (mEmptyView == null) { - return; - } - - // Cancel any pending task to show the loading animation and show the list of - // apps directly. - mFgHandler.removeCallbacks(mShowLoadingContainerRunnable); - handleLoadingContainer(false /* showContent */, true /* showEmpty */, animate); - } - - /** - * Shows loading view and hides content view & empty view. - */ - public void showLoadingView() { - handleLoadingContainer(false /* showContent */, false /* showEmpty */, false /* animate */); + handleLoadingContainer(true /* show */, animate); } public void showLoadingViewDelayed() { @@ -89,9 +57,8 @@ public class LoadingViewController { mShowLoadingContainerRunnable, DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS); } - private void handleLoadingContainer(boolean showContent, boolean showEmpty, boolean animate) { - handleLoadingContainer(mLoadingView, mContentView, mEmptyView, - showContent, showEmpty, animate); + public void handleLoadingContainer(boolean done, boolean animate) { + handleLoadingContainer(mLoadingView, mContentView, done, animate); } /** @@ -108,25 +75,6 @@ public class LoadingViewController { setViewShown(content, done, animate); } - /** - * Show/hide loading view and content view and empty view. - * - * @param loading The loading spinner view - * @param content The content view - * @param empty The empty view shows no item summary to users. - * @param showContent If true, content is set visible and loading is set invisible. - * @param showEmpty If true, empty is set visible and loading is set invisible. - * @param animate Whether or not content/loading views should animate in/out. - */ - public static void handleLoadingContainer(View loading, View content, View empty, - boolean showContent, boolean showEmpty, boolean animate) { - if (empty != null) { - setViewShown(empty, showEmpty, animate); - } - setViewShown(content, showContent, animate); - setViewShown(loading, !showContent && !showEmpty, animate); - } - private static void setViewShown(final View view, boolean shown, boolean animate) { if (animate) { Animation animation = AnimationUtils.loadAnimation(view.getContext(), diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java index 25eca7adb22..86f5fe83c6c 100644 --- a/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java +++ b/tests/robotests/src/com/android/settings/applications/manageapplications/ManageApplicationsTest.java @@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -48,6 +49,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.SearchView; import androidx.fragment.app.FragmentActivity; @@ -153,6 +155,22 @@ public class ManageApplicationsTest { assertThat(mMenu.findItem(R.id.sort_order_frequent_notification).isVisible()).isFalse(); } + @Test + public void onCreateView_shouldNotShowLoadingContainer() { + ReflectionHelpers.setField(mFragment, "mResetAppsHelper", mock(ResetAppsHelper.class)); + doNothing().when(mFragment).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); + + mFragment.onCreateView(layoutInflater, mock(ViewGroup.class), null); + + verify(loadingContainer, never()).setVisibility(View.VISIBLE); + } + @Test public void onCreateOptionsMenu_shouldSetSearchQueryListener() { final SearchView searchView = mock(SearchView.class); @@ -203,6 +221,7 @@ public class ManageApplicationsTest { @Test public void updateLoading_appLoaded_shouldNotDelayCallToHandleLoadingContainer() { ReflectionHelpers.setField(mFragment, "mLoadingContainer", mock(View.class)); + ReflectionHelpers.setField(mFragment, "mListContainer", mock(View.class)); final ManageApplications.ApplicationsAdapter adapter = spy(new ManageApplications.ApplicationsAdapter(mState, mFragment, AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); @@ -224,6 +243,7 @@ public class ManageApplicationsTest { @Test public void updateLoading_appNotLoaded_shouldDelayCallToHandleLoadingContainer() { ReflectionHelpers.setField(mFragment, "mLoadingContainer", mock(View.class)); + ReflectionHelpers.setField(mFragment, "mListContainer", mock(View.class)); final ManageApplications.ApplicationsAdapter adapter = spy(new ManageApplications.ApplicationsAdapter(mState, mFragment, AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); @@ -252,6 +272,7 @@ public class ManageApplicationsTest { when(listContainer.getVisibility()).thenReturn(View.INVISIBLE); when(listContainer.getContext()).thenReturn(context); ReflectionHelpers.setField(mFragment, "mLoadingContainer", loadingContainer); + ReflectionHelpers.setField(mFragment, "mListContainer", listContainer); final ManageApplications.ApplicationsAdapter adapter = spy(new ManageApplications.ApplicationsAdapter(mState, mFragment, AppFilterRegistry.getInstance().get(FILTER_APPS_ALL), new Bundle())); @@ -275,7 +296,7 @@ public class ManageApplicationsTest { adapter.onRebuildComplete(null); - verify(loadingViewController).showEmpty(false /* animate */); + verify(loadingViewController).showContent(true /* animate */); } @Test @@ -283,16 +304,15 @@ public class ManageApplicationsTest { final String query = "Test"; final RecyclerView recyclerView = mock(RecyclerView.class); final View emptyView = mock(View.class); - final View loadingContainer = mock(View.class); ReflectionHelpers.setField(mFragment, "mRecyclerView", recyclerView); ReflectionHelpers.setField(mFragment, "mEmptyView", emptyView); - ReflectionHelpers.setField(mFragment, "mLoadingContainer", loadingContainer); final SearchView searchView = mock(SearchView.class); ReflectionHelpers.setField(mFragment, "mSearchView", searchView); when(searchView.isVisibleToUser()).thenReturn(true); when(searchView.getQuery()).thenReturn(query); final View listContainer = mock(View.class); when(listContainer.getVisibility()).thenReturn(View.VISIBLE); + ReflectionHelpers.setField(mFragment, "mListContainer", listContainer); ReflectionHelpers.setField( mFragment, "mFilterAdapter", mock(ManageApplications.FilterSpinnerAdapter.class)); final ArrayList appList = new ArrayList<>(); @@ -471,6 +491,8 @@ public class ManageApplicationsTest { mFragment.mFilterAdapter.updateFilterView(true); assertThat(mFragment.mSpinnerHeader.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mFragment.mRecyclerView.getPaddingTop()).isEqualTo( + mContext.getResources().getDimensionPixelSize(R.dimen.app_bar_height)); } @Test