Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a1a84e65 authored by Fan Zhang's avatar Fan Zhang
Browse files

Make dashboard tile refresh more effcient.

Instead of removing and re-adding all dashboard tiles, figure out a diff
and rebind/add/remove as necessary.

Bug: 32255863
Test: RunSettingsRoboTests
Change-Id: I9d87ba30ab746257d0ea71282951348ebc4e8965
parent 8b5bca59
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.settings.dashboard;

import android.content.Context;
import android.support.v7.preference.Preference;

import com.android.settingslib.drawer.DashboardCategory;
@@ -53,4 +54,10 @@ public interface DashboardFeatureProvider {
     * Returns an unique string key for the tile.
     */
    String getDashboardKeyForTile(Tile tile);

    /**
     * Returns a {@link ProgressiveDisclosureMixin} for specified fragment.
     */
    ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context,
            DashboardFragment fragment);
}
+6 −0
Original line number Diff line number Diff line
@@ -72,4 +72,10 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
        sb.append(component.getClassName());
        return sb.toString();
    }

    @Override
    public ProgressiveDisclosureMixin getProgressiveDisclosureMixin(Context context,
            DashboardFragment fragment) {
        return new ProgressiveDisclosureMixin(context, this, fragment);
    }
}
+85 −54
Original line number Diff line number Diff line
@@ -22,7 +22,9 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -41,6 +43,7 @@ import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.SettingsDrawerActivity;
import com.android.settingslib.drawer.Tile;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -69,7 +72,8 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        super.onAttach(context);
        mDashboardFeatureProvider =
                FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
        mProgressiveDisclosureMixin = new ProgressiveDisclosureMixin(context, this);
        mProgressiveDisclosureMixin = mDashboardFeatureProvider
                .getProgressiveDisclosureMixin(context, this);
        getLifecycle().addObserver(mProgressiveDisclosureMixin);

        final List<PreferenceController> controllers = getPreferenceControllers(context);
@@ -81,6 +85,24 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        }
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        // Set ComparisonCallback so we get better animation when list changes.
        getPreferenceManager().setPreferenceComparisonCallback(
                new PreferenceManager.SimplePreferenceComparisonCallback());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final View view = super.onCreateView(inflater, container, savedInstanceState);
        if (mDashboardFeatureProvider.isEnabled()) {
            getListView().addItemDecoration(mDividerDecoration);
        }
        return view;
    }

    @Override
    public void onCategoriesChanged() {
        final DashboardCategory category =
@@ -98,6 +120,18 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        refreshAllPreferences(getLogTag());
    }

    @Override
    public void setDivider(Drawable divider) {
        if (mDashboardFeatureProvider.isEnabled()) {
            // Intercept divider and set it transparent so system divider decoration is disabled.
            // We will use our decoration to draw divider more intelligently.
            mDividerDecoration.setDivider(divider);
            super.setDivider(new ColorDrawable(Color.TRANSPARENT));
        } else {
            super.setDivider(divider);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
@@ -210,16 +244,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final View view = super.onCreateView(inflater, container, savedInstanceState);
        if (mDashboardFeatureProvider.isEnabled()) {
            getListView().addItemDecoration(mDividerDecoration);
        }
        return view;
    }

    /**
     * Update state of each preference managed by PreferenceController.
     */
@@ -238,18 +262,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        }
    }

    @Override
    public void setDivider(Drawable divider) {
        if (mDashboardFeatureProvider.isEnabled()) {
            // Intercept divider and set it transparent so system divider decoration is disabled.
            // We will use our decoration to draw divider more intelligently.
            mDividerDecoration.setDivider(divider);
            super.setDivider(new ColorDrawable(Color.TRANSPARENT));
        } else {
            super.setDivider(divider);
        }
    }

    /**
     * Refresh all preference items, including both static prefs from xml, and dynamic items from
     * DashboardCategory.
@@ -275,18 +287,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
    /**
     * Refresh preference items backed by DashboardCategory.
     */
    private void refreshDashboardTiles(final String TAG) {
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    void refreshDashboardTiles(final String TAG) {
        final PreferenceScreen screen = getPreferenceScreen();
        for (String key : mDashboardTilePrefKeys) {
            // Remove tiles from screen
            mProgressiveDisclosureMixin.removePreference(screen, key);
        }
        mDashboardTilePrefKeys.clear();

        final Context context = getContext();
        final DashboardCategory category =
                mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
        if (category == null) {
            Log.d(TAG, "NO dynamic tiles for " + TAG);
            Log.d(TAG, "NO dashboard tiles for " + TAG);
            return;
        }
        List<Tile> tiles = category.tiles;
@@ -294,6 +303,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
            Log.d(TAG, "tile list is empty, skipping category " + category.title);
            return;
        }
        // Create a list to track which tiles are to be removed.
        final List<String> remove = new ArrayList<>(mDashboardTilePrefKeys);

        // There are dashboard tiles, so we need to install SummaryLoader.
        if (mSummaryLoader != null) {
            mSummaryLoader.release();
@@ -307,8 +319,29 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
                Log.d(TAG, "tile does not contain a key, skipping " + tile);
                continue;
            }
            mDashboardTilePrefKeys.add(key);
            if (mDashboardTilePrefKeys.contains(key)) {
                // Have the key already, will rebind.
                final Preference preference = mProgressiveDisclosureMixin.findPreference(
                        screen, key);
                bindPreferenceToTile(context, preference, tile, key);
            } else {
                // Don't have this key, add it.
                final Preference pref = new DashboardTilePreference(context);
                bindPreferenceToTile(context, pref, tile, key);
                mProgressiveDisclosureMixin.addPreference(screen, pref);
                mDashboardTilePrefKeys.add(key);
            }
            remove.remove(key);
        }
        // Finally remove tiles that are gone.
        for (String key : remove) {
            mDashboardTilePrefKeys.remove(key);
            mProgressiveDisclosureMixin.removePreference(screen, key);
        }
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    void bindPreferenceToTile(Context context, Preference pref, Tile tile, String key) {
        pref.setTitle(tile.title);
        pref.setKey(key);
        pref.setSummary(tile.summary);
@@ -332,7 +365,5 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
        // (larger value has higher priority). However pref order defines smaller value has
        // higher priority.
        pref.setOrder(-tile.priority);
            mProgressiveDisclosureMixin.addPreference(screen, pref);
        }
    }
}
+45 −20
Original line number Diff line number Diff line
@@ -28,9 +28,9 @@ import android.util.Log;
import com.android.settings.core.lifecycle.LifecycleObserver;
import com.android.settings.core.lifecycle.events.OnCreate;
import com.android.settings.core.lifecycle.events.OnSaveInstanceState;
import com.android.settings.overlay.FeatureFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickListener,
@@ -43,18 +43,19 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
    private int mTileLimit = DEFAULT_TILE_LIMIT;

    private final DashboardFeatureProvider mDashboardFeatureProvider;
    private final List<Preference> collapsedPrefs = new ArrayList<>();
    // Collapsed preference sorted by order.
    private final List<Preference> mCollapsedPrefs = new ArrayList<>();
    private final ExpandPreference mExpandButton;
    private final PreferenceFragment mFragment;

    private boolean mUserExpanded;

    public ProgressiveDisclosureMixin(Context context, PreferenceFragment fragment) {
    public ProgressiveDisclosureMixin(Context context,
            DashboardFeatureProvider dashboardFeatureProvider, PreferenceFragment fragment) {
        mFragment = fragment;
        mExpandButton = new ExpandPreference(context);
        mExpandButton.setOnPreferenceClickListener(this);
        mDashboardFeatureProvider = FeatureFactory.getFactory(context)
                .getDashboardFeatureProvider(context);
        mDashboardFeatureProvider = dashboardFeatureProvider;
    }

    @Override
@@ -75,10 +76,10 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
            final PreferenceScreen screen = mFragment.getPreferenceScreen();
            if (screen != null) {
                screen.removePreference(preference);
                for (Preference pref : collapsedPrefs) {
                for (Preference pref : mCollapsedPrefs) {
                    screen.addPreference(pref);
                }
                collapsedPrefs.clear();
                mCollapsedPrefs.clear();
                mUserExpanded = true;
            }
        }
@@ -96,7 +97,7 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
     * Whether the controller is in collapsed state.
     */
    public boolean isCollapsed() {
        return !collapsedPrefs.isEmpty();
        return !mCollapsedPrefs.isEmpty();
    }

    /**
@@ -115,7 +116,7 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
        if (!shouldCollapse(screen)) {
            return;
        }
        if (!collapsedPrefs.isEmpty()) {
        if (!mCollapsedPrefs.isEmpty()) {
            Log.w(TAG, "collapsed list should ALWAYS BE EMPTY before collapsing!");
        }

@@ -134,12 +135,27 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
    public void addPreference(PreferenceScreen screen, Preference pref) {
        // Either add to screen, or to collapsed list.
        if (isCollapsed()) {
            // Already collapsed, add to collapsed list.
            // insert the preference to right position.
            final int lastPreferenceIndex = screen.getPreferenceCount() - 2;
            if (lastPreferenceIndex >= 0) {
                final Preference lastPreference = screen.getPreference(lastPreferenceIndex);
                if (lastPreference.getOrder() > pref.getOrder()) {
                    // insert to screen and move the last pref to collapsed list.
                    screen.removePreference(lastPreference);
                    screen.addPreference(pref);
                    addToCollapsedList(lastPreference);
                } else {
                    // Insert to collapsed list.
                    addToCollapsedList(pref);
                }
            } else {
                // Couldn't find last preference on screen, just add to collapsed list.
                addToCollapsedList(pref);
            }
        } else if (shouldCollapse(screen)) {
            // About to have too many tiles on scree, collapse and add pref to collapsed list.
            screen.addPreference(pref);
            collapse(screen);
            addToCollapsedList(pref);
        } else {
            // No need to collapse, add to screen directly.
            screen.addPreference(pref);
@@ -158,11 +174,11 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
            return;
        }
        // Didn't find on screen, try removing from collapsed list.
        for (int i = 0; i < collapsedPrefs.size(); i++) {
            final Preference pref = collapsedPrefs.get(i);
        for (int i = 0; i < mCollapsedPrefs.size(); i++) {
            final Preference pref = mCollapsedPrefs.get(i);
            if (TextUtils.equals(key, pref.getKey())) {
                collapsedPrefs.remove(pref);
                if (collapsedPrefs.isEmpty()) {
                mCollapsedPrefs.remove(pref);
                if (mCollapsedPrefs.isEmpty()) {
                    // Removed last element, remove expand button too.
                    screen.removePreference(mExpandButton);
                }
@@ -179,8 +195,8 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
        if (preference != null) {
            return preference;
        }
        for (int i = 0; i < collapsedPrefs.size(); i++) {
            final Preference pref = collapsedPrefs.get(i);
        for (int i = 0; i < mCollapsedPrefs.size(); i++) {
            final Preference pref = mCollapsedPrefs.get(i);
            if (TextUtils.equals(key, pref.getKey())) {
                return pref;
            }
@@ -192,9 +208,18 @@ public class ProgressiveDisclosureMixin implements Preference.OnPreferenceClickL
    /**
     * Add preference to collapsed list.
     */
    @VisibleForTesting
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    void addToCollapsedList(Preference preference) {
        collapsedPrefs.add(preference);
        // Insert preference based on it's order.
        int insertionIndex = Collections.binarySearch(mCollapsedPrefs, preference);
        if (insertionIndex < 0) {
            insertionIndex = insertionIndex * -1 - 1;
        }
        mCollapsedPrefs.add(insertionIndex, preference);
    }

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    List<Preference> getCollapsedPrefs() {
        return mCollapsedPrefs;
    }
}
+35 −3
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@
package com.android.settings.dashboard;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;

import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.core.PreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -44,6 +47,7 @@ import java.util.List;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -59,6 +63,8 @@ public class DashboardFragmentTest {
    private DashboardCategory mDashboardCategory;
    @Mock
    private FakeFeatureFactory mFakeFeatureFactory;
    @Mock
    private ProgressiveDisclosureMixin mDisclosureMixin;
    private TestFragment mTestFragment;

    @Before
@@ -69,9 +75,12 @@ public class DashboardFragmentTest {
        mDashboardCategory.tiles = new ArrayList<>();
        mDashboardCategory.tiles.add(new Tile());
        mTestFragment = new TestFragment(ShadowApplication.getInstance().getApplicationContext());
        mTestFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
        when(mFakeFeatureFactory.dashboardFeatureProvider
                .getProgressiveDisclosureMixin(any(Context.class), eq(mTestFragment)))
                .thenReturn(mDisclosureMixin);
        when(mFakeFeatureFactory.dashboardFeatureProvider.getTilesForCategory(anyString()))
                .thenReturn(mDashboardCategory);
        mTestFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
    }

    @Test
@@ -87,11 +96,14 @@ public class DashboardFragmentTest {

    @Test
    public void displayTilesAsPreference_shouldAddTilesWithIntent() {
        when(mFakeFeatureFactory.dashboardFeatureProvider.getTilesForCategory(anyString()))
                .thenReturn(mDashboardCategory);
        when(mFakeFeatureFactory.dashboardFeatureProvider.getDashboardKeyForTile(any(Tile.class)))
                .thenReturn("test_key");
        mTestFragment.onCreatePreferences(new Bundle(), "rootKey");

        verify(mTestFragment.mScreen).addPreference(any(DashboardTilePreference.class));
        verify(mDisclosureMixin).addPreference(any(PreferenceScreen.class),
                any(DashboardTilePreference.class));
    }

    @Test
@@ -109,6 +121,27 @@ public class DashboardFragmentTest {
        verify(mTestFragment.mScreen, never()).addPreference(any(DashboardTilePreference.class));
    }

    @Test
    public void bindPreference_shouldBindAllData() {
        final Preference preference = new Preference(
                ShadowApplication.getInstance().getApplicationContext());
        final Tile tile = new Tile();
        tile.title = "title";
        tile.summary = "summary";
        tile.icon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565));
        tile.metaData = new Bundle();
        tile.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI");
        tile.priority = 10;
        mTestFragment.bindPreferenceToTile(mContext, preference, tile, "123");

        assertThat(preference.getTitle()).isEqualTo(tile.title);
        assertThat(preference.getSummary()).isEqualTo(tile.summary);
        assertThat(preference.getIcon()).isNotNull();
        assertThat(preference.getFragment())
                .isEqualTo(tile.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS));
        assertThat(preference.getOrder()).isEqualTo(-tile.priority);
    }

    public static class TestPreferenceController extends PreferenceController {

        public TestPreferenceController(Context context) {
@@ -139,7 +172,6 @@ public class DashboardFragmentTest {
    public static class TestFragment extends DashboardFragment {

        private final Context mContext;
        @Mock
        public PreferenceScreen mScreen;

        public TestFragment(Context context) {
Loading