Reduce jank around loading view when opening data usage UI
Change-Id: I3d23d8160b046de8fe125ba0697b7b3d7786453c
Fix: 28181319
Test: robotests
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 5f22545..a3d26af 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -49,6 +49,7 @@
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.Instrumentable;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.CustomDialogPreference;
import com.android.settingslib.CustomEditTextPreference;
import com.android.settingslib.HelpUtils;
@@ -240,14 +241,11 @@
unregisterObserverIfNeeded();
}
- public void showLoadingWhenEmpty() {
- View loading = getView().findViewById(R.id.loading_container);
- setEmptyView(loading);
- }
-
public void setLoading(boolean loading, boolean animate) {
- View loading_container = getView().findViewById(R.id.loading_container);
- Utils.handleLoadingContainer(loading_container, getListView(), !loading, animate);
+ View loadingContainer = getView().findViewById(R.id.loading_container);
+ LoadingViewController.handleLoadingContainer(loadingContainer, getListView(),
+ !loading /* done */,
+ animate);
}
public void registerObserverIfNeeded() {
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index cb8d3c2..b6a958c 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -949,43 +949,6 @@
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,
- boolean animate) {
- setViewShown(loading, !done, animate);
- setViewShown(doneLoading, done, animate);
- }
-
- private static void setViewShown(final View view, boolean shown, boolean animate) {
- if (animate) {
- Animation animation = AnimationUtils.loadAnimation(view.getContext(),
- shown ? android.R.anim.fade_in : android.R.anim.fade_out);
- if (shown) {
- view.setVisibility(View.VISIBLE);
- } else {
- animation.setAnimationListener(new AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- view.setVisibility(View.INVISIBLE);
- }
- });
- }
- view.startAnimation(animation);
- } else {
- view.clearAnimation();
- view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
/**
* Returns the application info of the currently installed MDM package.
*/
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 07632da..720f826 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -81,6 +81,7 @@
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
+import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -848,6 +849,7 @@
private final AppStateBaseBridge mExtraInfoBridge;
private final Handler mBgHandler;
private final Handler mFgHandler;
+ private final LoadingViewController mLoadingViewController;
private int mFilterMode;
private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
@@ -893,12 +895,6 @@
}
};
- private Runnable mShowLoadingContainerRunnable = new Runnable() {
- public void run() {
- Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
- mManageApplications.mListContainer, false /* done */, false /* animate */);
- }
- };
public ApplicationsAdapter(ApplicationsState state, ManageApplications manageApplications,
int filterMode) {
@@ -907,6 +903,10 @@
mBgHandler = new Handler(mState.getBackgroundLooper());
mSession = state.newSession(this);
mManageApplications = manageApplications;
+ mLoadingViewController = new LoadingViewController(
+ mManageApplications.mLoadingContainer,
+ mManageApplications.mListContainer
+ );
mContext = manageApplications.getActivity();
mPm = mContext.getPackageManager();
mFilterMode = filterMode;
@@ -1108,11 +1108,7 @@
if (mSession.getAllApps().size() != 0
&& 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,
- mManageApplications.mListContainer, true, true);
+ mLoadingViewController.showContent(true /* animate */);
}
if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
// No enabled or disabled filters for usage access.
@@ -1166,11 +1162,9 @@
void updateLoading() {
final boolean appLoaded = mHasReceivedLoadEntries && mSession.getAllApps().size() != 0;
if (appLoaded) {
- Utils.handleLoadingContainer(mManageApplications.mLoadingContainer,
- mManageApplications.mListContainer, true /* done */, false /* animate */);
+ mLoadingViewController.showContent(false /* animate */);
} else {
- mFgHandler.postDelayed(
- mShowLoadingContainerRunnable, DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS);
+ mLoadingViewController.showLoadingViewDelayed();
}
}
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
index 736eafb..634fefd 100644
--- a/src/com/android/settings/applications/RunningServices.java
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -15,7 +15,6 @@
*/
package com.android.settings.applications;
-import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -27,7 +26,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
+import com.android.settings.widget.LoadingViewController;
public class RunningServices extends SettingsPreferenceFragment {
@@ -37,6 +36,7 @@
private RunningProcessesView mRunningProcessesView;
private Menu mOptionsMenu;
private View mLoadingContainer;
+ private LoadingViewController mLoadingViewController;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -47,12 +47,13 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.manage_applications_running, null);
- mRunningProcessesView = (RunningProcessesView) rootView.findViewById(
- R.id.running_processes);
+ mRunningProcessesView = rootView.findViewById(R.id.running_processes);
mRunningProcessesView.doCreate();
mLoadingContainer = rootView.findViewById(R.id.loading_container);
+ mLoadingViewController = new LoadingViewController(
+ mLoadingContainer, mRunningProcessesView);
return rootView;
}
@@ -71,7 +72,7 @@
public void onResume() {
super.onResume();
boolean haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail);
- Utils.handleLoadingContainer(mLoadingContainer, mRunningProcessesView, haveData, false);
+ mLoadingViewController.handleLoadingContainer(haveData /* done */, false /* animate */);
}
@Override
@@ -115,7 +116,7 @@
private final Runnable mRunningProcessesAvail = new Runnable() {
@Override
public void run() {
- Utils.handleLoadingContainer(mLoadingContainer, mRunningProcessesView, true, true);
+ mLoadingViewController.showContent(true /* animate */);
}
};
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index de92154..f0048a3 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -50,6 +50,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
+import com.android.settings.widget.LoadingViewController;
import com.android.settingslib.AppItem;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
@@ -96,13 +97,13 @@
};
private INetworkStatsSession mStatsSession;
-
private ChartDataUsagePreference mChart;
private NetworkTemplate mTemplate;
private int mSubId;
private ChartData mChartData;
+ private LoadingViewController mLoadingViewController;
private UidDetailProvider mUidDetailProvider;
private CycleAdapter mCycleAdapter;
private Spinner mCycleSpinner;
@@ -110,6 +111,7 @@
private PreferenceGroup mApps;
private View mHeader;
+
@Override
public int getMetricsCategory() {
return MetricsEvent.DATA_USAGE_LIST;
@@ -176,7 +178,10 @@
mCycleSpinner.setSelection(position);
}
}, mCycleListener, true);
- setLoading(true, false);
+
+ mLoadingViewController = new LoadingViewController(
+ getView().findViewById(R.id.loading_container), getListView());
+ mLoadingViewController.showLoadingViewDelayed();
}
@Override
@@ -523,7 +528,7 @@
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
- setLoading(false, true);
+ mLoadingViewController.showContent(false /* animate */);
mChartData = data;
mChart.setNetworkStats(mChartData.network);
diff --git a/src/com/android/settings/widget/LoadingViewController.java b/src/com/android/settings/widget/LoadingViewController.java
new file mode 100644
index 0000000..294e55e
--- /dev/null
+++ b/src/com/android/settings/widget/LoadingViewController.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.widget;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+/**
+ * A helper class that manages show/hide loading spinner.
+ */
+public class LoadingViewController {
+
+ private static final long DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS = 100L;
+
+ public final Handler mFgHandler;
+ public final View mLoadingView;
+ public final View mContentView;
+
+ public LoadingViewController(View loadingView, View contentView) {
+ mLoadingView = loadingView;
+ mContentView = contentView;
+ mFgHandler = new Handler(Looper.getMainLooper());
+ }
+
+ private Runnable mShowLoadingContainerRunnable = new Runnable() {
+ public void run() {
+ handleLoadingContainer(false /* done */, false /* animate */);
+ }
+ };
+
+ 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 /* show */, animate);
+ }
+
+ public void showLoadingViewDelayed() {
+ mFgHandler.postDelayed(
+ mShowLoadingContainerRunnable, DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS);
+ }
+
+ public void handleLoadingContainer(boolean done, boolean animate) {
+ handleLoadingContainer(mLoadingView, mContentView, done, animate);
+ }
+
+ /**
+ * Show/hide loading view and content view.
+ *
+ * @param loading The loading spinner view
+ * @param content The content view
+ * @param done If true, content 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, boolean done,
+ boolean animate) {
+ setViewShown(loading, !done, animate);
+ setViewShown(content, done, animate);
+ }
+
+ private static void setViewShown(final View view, boolean shown, boolean animate) {
+ if (animate) {
+ Animation animation = AnimationUtils.loadAnimation(view.getContext(),
+ shown ? android.R.anim.fade_in : android.R.anim.fade_out);
+ if (shown) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ view.startAnimation(animation);
+ } else {
+ view.clearAnimation();
+ view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+}