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

Commit d9644676 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix NPE when building suggestion identifier."

parents 0fea0d33 264c0c2b
Loading
Loading
Loading
Loading
+42 −62
Original line number Diff line number Diff line
@@ -40,8 +40,8 @@ import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapterUtils;
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.SuggestionParser;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;

@@ -60,6 +60,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
    private final Context mContext;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private final DashboardFeatureProvider mDashboardFeatureProvider;
    private final SuggestionFeatureProvider mSuggestionFeatureProvider;
    private final ArrayList<String> mSuggestionsShownLogged;
    private boolean mFirstFrameDrawn;

@@ -97,17 +98,17 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
        }
    };

    public DashboardAdapter(Context context, SuggestionParser parser,
            MetricsFeatureProvider metricsFeatureProvider, Bundle savedInstanceState,
    public DashboardAdapter(Context context, Bundle savedInstanceState,
            List<Condition> conditions) {
        List<Tile> suggestions = null;
        List<DashboardCategory> categories = null;
        int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT;

        mContext = context;
        mMetricsFeatureProvider = metricsFeatureProvider;
        mDashboardFeatureProvider = FeatureFactory.getFactory(context)
                .getDashboardFeatureProvider(context);
        final FeatureFactory factory = FeatureFactory.getFactory(context);
        mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
        mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
        mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
        mCache = new IconCache(context);

        setHasStableIds(true);
@@ -120,7 +121,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
            mSuggestionsShownLogged = savedInstanceState.getStringArrayList(
                    STATE_SUGGESTIONS_SHOWN_LOGGED);
        } else {
            mSuggestionsShownLogged = new ArrayList<String>();
            mSuggestionsShownLogged = new ArrayList<>();
        }

        mDashboardData = new DashboardData.Builder()
@@ -173,11 +174,11 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
        }
        if (shownSuggestions != null) {
            for (Tile suggestion : shownSuggestions) {
                String suggestionId = getSuggestionIdentifier(mContext, suggestion);
                final String identifier = mSuggestionFeatureProvider.getSuggestionIdentifier(
                        mContext, suggestion);
                mMetricsFeatureProvider.action(
                        mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
                        getSuggestionIdentifier(mContext, suggestion));
                mSuggestionsShownLogged.add(getSuggestionIdentifier(mContext, suggestion));
                        mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, identifier);
                mSuggestionsShownLogged.add(identifier);
            }
        }
    }
@@ -237,7 +238,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
                break;
            case R.layout.suggestion_tile:
                final Tile suggestion = (Tile) mDashboardData.getItemEntityByPosition(position);
                String suggestionId = getSuggestionIdentifier(mContext, suggestion);
                final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
                        mContext, suggestion);
                // This is for cases when a suggestion is dismissed and the next one comes to view
                if (!mSuggestionsShownLogged.contains(suggestionId)) {
                    mMetricsFeatureProvider.action(
@@ -245,14 +247,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
                    mSuggestionsShownLogged.add(suggestionId);
                }
                onBindTile(holder, suggestion);
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                holder.itemView.setOnClickListener(v -> {
                    mMetricsFeatureProvider.action(mContext,
                                MetricsEvent.ACTION_SETTINGS_SUGGESTION,
                                DashboardAdapter.getSuggestionIdentifier(mContext, suggestion));
                            MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId);
                    ((SettingsActivity) mContext).startSuggestion(suggestion.intent);
                    }
                });
                break;
            case R.layout.condition_card:
@@ -260,13 +258,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
                        == mDashboardData.getExpandedCondition();
                ConditionAdapterUtils.bindViews(
                        (Condition) mDashboardData.getItemEntityByPosition(position),
                        holder, isExpanded, mConditionClickListener,
                        new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                onExpandClick(v);
                            }
                        });
                        holder, isExpanded, mConditionClickListener, v -> onExpandClick(v));
                break;
        }
    }
@@ -291,7 +283,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
            return;
        }
        for (Tile suggestion : mDashboardData.getSuggestions()) {
            String suggestionId = getSuggestionIdentifier(mContext, suggestion);
            String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
                    mContext, suggestion);
            if (mSuggestionsShownLogged.contains(suggestionId)) {
                mMetricsFeatureProvider.action(
                        mContext, MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, suggestionId);
@@ -320,16 +313,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
        return mDashboardData.getItemEntityById(itemId);
    }

    public static String getSuggestionIdentifier(Context context, Tile suggestion) {
        String packageName = suggestion.intent.getComponent().getPackageName();
        if (packageName.equals(context.getPackageName())) {
            // Since Settings provides several suggestions, fill in the class instead of the
            // package for these.
            packageName = suggestion.intent.getComponent().getClassName();
        }
        return packageName;
    }

    private void notifyDashboardDataChanged(DashboardData prevData) {
        if (mFirstFrameDrawn && prevData != null) {
            final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DashboardData
@@ -394,16 +377,14 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
            holder.summary.setText(
                    mContext.getString(R.string.suggestions_summary, undisplayedSuggestionCount));
        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
        holder.itemView.setOnClickListener(v -> {
            final int suggestionMode;
            if (moreSuggestions) {
                suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED;

                for (Tile suggestion : mDashboardData.getSuggestions()) {
                        String suggestionId =
                                DashboardAdapter.getSuggestionIdentifier(mContext, suggestion);
                    final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
                            mContext, suggestion);
                    if (!mSuggestionsShownLogged.contains(suggestionId)) {
                        mMetricsFeatureProvider.action(
                                mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
@@ -420,7 +401,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
                    .setSuggestionMode(suggestionMode)
                    .build();
            notifyDashboardDataChanged(prevData);
            }
        });
    }

+3 −4
Original line number Diff line number Diff line
@@ -190,8 +190,7 @@ public class DashboardSummary extends InstrumentedFragment
        mDashboard.addItemDecoration(new DashboardDecorator(getContext()));
        mDashboard.setListener(this);
        Log.d(TAG, "adapter created");
        mAdapter = new DashboardAdapter(getContext(), mSuggestionParser, mMetricsFeatureProvider,
                bundle, mConditionManager.getConditions());
        mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions());
        mDashboard.setAdapter(mAdapter);
        mSuggestionDismissHandler = new SuggestionDismissController(
                getContext(), mDashboard, mSuggestionParser, mAdapter);
@@ -245,8 +244,8 @@ public class DashboardSummary extends InstrumentedFragment
            if (isSmartSuggestionEnabled) {
                List<String> suggestionIds = new ArrayList<>(suggestions.size());
                for (Tile suggestion : suggestions) {
                    suggestionIds.add(
                            DashboardAdapter.getSuggestionIdentifier(context, suggestion));
                    suggestionIds.add(mSuggestionFeatureProvider.getSuggestionIdentifier(
                            context, suggestion));
                }
                // TODO: create a Suggestion class to maintain the id and other info
                mSuggestionFeatureProvider.rankSuggestions(suggestions, suggestionIds);
+5 −0
Original line number Diff line number Diff line
@@ -49,4 +49,9 @@ public interface SuggestionFeatureProvider {
     * Dismisses a suggestion.
     */
    void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion);

    /**
     * Returns an identifier for the suggestion
     */
    String getSuggestionIdentifier(Context context, Tile suggestion);
}
+18 −4
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.content.pm.PackageManager;

import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.DashboardAdapter;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.SuggestionParser;
import com.android.settingslib.drawer.Tile;
@@ -50,9 +49,10 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider


    public SuggestionFeatureProviderImpl(Context context) {
        final Context appContext = context.getApplicationContext();
        mSuggestionRanker = new SuggestionRanker(
                new SuggestionFeaturizer(new EventStore(context.getApplicationContext())));
        mMetricsFeatureProvider = FeatureFactory.getFactory(context)
                new SuggestionFeaturizer(new EventStore(appContext)));
        mMetricsFeatureProvider = FeatureFactory.getFactory(appContext)
                .getMetricsFeatureProvider();
    }

@@ -68,7 +68,7 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
        }
        mMetricsFeatureProvider.action(
                context, MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
                DashboardAdapter.getSuggestionIdentifier(context, suggestion));
                getSuggestionIdentifier(context, suggestion));

        final boolean isSmartSuggestionEnabled = isSmartSuggestionEnabled(context);
        if (!parser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) {
@@ -81,4 +81,18 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
        parser.markCategoryDone(suggestion.category);
    }

    @Override
    public String getSuggestionIdentifier(Context context, Tile suggestion) {
        if (suggestion.intent == null || suggestion.intent.getComponent() == null) {
            return "unknown_suggestion";
        }
        String packageName = suggestion.intent.getComponent().getPackageName();
        if (packageName.equals(context.getPackageName())) {
            // Since Settings provides several suggestions, fill in the class instead of the
            // package for these.
            packageName = suggestion.intent.getComponent().getClassName();
        }
        return packageName;
    }

}
+38 −42
Original line number Diff line number Diff line
@@ -21,35 +21,32 @@ import android.content.Intent;
import android.content.res.Resources;
import android.view.View;
import android.widget.FrameLayout;

import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

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

import static com.google.common.truth.Truth.assertThat;
import org.mockito.Matchers;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -64,20 +61,19 @@ import static org.mockito.Mockito.when;
        })
public class DashboardAdapterTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private Context mContext;
    @Mock
    private View mView;
    @Mock
    private Condition mCondition;
    @Mock
    private MetricsFeatureProvider mMetricsFeatureProvider;
    @Mock
    private Resources mResources;
    @Mock
    private DashboardData mDashboardData;
    @Captor
    private ArgumentCaptor<Integer> mActionCategoryCaptor = ArgumentCaptor.forClass(Integer.class);
    @Captor
    private ArgumentCaptor<String> mActionPackageCaptor = ArgumentCaptor.forClass(String.class);
    private FakeFeatureFactory mFactory;
    private DashboardAdapter mDashboardAdapter;
    private DashboardAdapter.DashboardItemHolder mSuggestionHolder;
    private DashboardData.SuggestionHeaderData mSuggestionHeaderData;
@@ -85,15 +81,20 @@ public class DashboardAdapterTest {
    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        Context context = RuntimeEnvironment.application;
        context = spy(context);
        when(context.getResources()).thenReturn(mResources);
        when(mResources
                .getQuantityString(any(int.class), any(int.class), Matchers.<Object>anyVararg()))
        FakeFeatureFactory.setupForTest(mContext);
        mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
        when(mFactory.suggestionsFeatureProvider
                .getSuggestionIdentifier(any(Context.class), any(Tile.class)))
                .thenAnswer(invocation -> {
                    final Object[] args = invocation.getArguments();
                    return ((Tile)args[1]).intent.getComponent().getPackageName();
                });

        when(mContext.getResources()).thenReturn(mResources);
        when(mResources.getQuantityString(any(int.class), any(int.class), any()))
                .thenReturn("");
        FakeFeatureFactory.setupForTest(context);
        mDashboardAdapter = new DashboardAdapter(context, null, mMetricsFeatureProvider,
                null, null);

        mDashboardAdapter = new DashboardAdapter(mContext, null, null);
        mSuggestionHeaderData = new DashboardData.SuggestionHeaderData(true, 1, 0);
        when(mView.getTag()).thenReturn(mCondition);
    }
@@ -109,7 +110,7 @@ public class DashboardAdapterTest {
    @Test
    public void testSuggestionsLogs_NotExpanded() {
        setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"}));
        verify(mMetricsFeatureProvider, times(2)).action(
        verify(mFactory.metricsFeatureProvider, times(2)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg2"};
@@ -117,15 +118,15 @@ public class DashboardAdapterTest {
                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION
        };
        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
        assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions);
        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
    }

    @Test
    public void testSuggestionsLogs_NotExpandedAndPaused() {
        setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"}));
        mDashboardAdapter.onPause();
        verify(mMetricsFeatureProvider, times(4)).action(
        verify(mFactory.metricsFeatureProvider, times(4)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg1", "pkg2"};
@@ -134,8 +135,8 @@ public class DashboardAdapterTest {
                MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION,
                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION,
                MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION};
        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
        assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions);
        assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages);
    }

    @Test
@@ -144,7 +145,7 @@ public class DashboardAdapterTest {
        mDashboardAdapter.onBindSuggestionHeader(
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        verify(mMetricsFeatureProvider, times(3)).action(
        verify(mFactory.metricsFeatureProvider, times(3)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3"};
@@ -164,7 +165,7 @@ public class DashboardAdapterTest {
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        mDashboardAdapter.onPause();
        verify(mMetricsFeatureProvider, times(6)).action(
        verify(mFactory.metricsFeatureProvider, times(6)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3"};
@@ -187,7 +188,7 @@ public class DashboardAdapterTest {
        mDashboardAdapter.onBindSuggestionHeader(
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        verify(mMetricsFeatureProvider, times(7)).action(
        verify(mFactory.metricsFeatureProvider, times(7)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{
@@ -213,7 +214,7 @@ public class DashboardAdapterTest {
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        mDashboardAdapter.onPause();
        verify(mMetricsFeatureProvider, times(10)).action(
        verify(mFactory.metricsFeatureProvider, times(10)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{
@@ -240,7 +241,7 @@ public class DashboardAdapterTest {
        mDashboardAdapter.onBindSuggestionHeader(
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        verify(mMetricsFeatureProvider, times(1)).action(
        verify(mFactory.metricsFeatureProvider, times(1)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1"};
@@ -258,7 +259,7 @@ public class DashboardAdapterTest {
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        mDashboardAdapter.onPause();
        verify(mMetricsFeatureProvider, times(2)).action(
        verify(mFactory.metricsFeatureProvider, times(2)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg1"};
@@ -277,7 +278,7 @@ public class DashboardAdapterTest {
        mDashboardAdapter.onBindSuggestionHeader(
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        verify(mMetricsFeatureProvider, times(3)).action(
        verify(mFactory.metricsFeatureProvider, times(3)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1"};
@@ -298,7 +299,7 @@ public class DashboardAdapterTest {
                mSuggestionHolder, mSuggestionHeaderData);
        mSuggestionHolder.itemView.callOnClick();
        mDashboardAdapter.onPause();
        verify(mMetricsFeatureProvider, times(4)).action(
        verify(mFactory.metricsFeatureProvider, times(4)).action(
                any(Context.class), mActionCategoryCaptor.capture(),
                mActionPackageCaptor.capture());
        String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1", "pkg1"};
@@ -313,23 +314,18 @@ public class DashboardAdapterTest {
    }

    private List<Tile> makeSuggestions(String[] pkgNames) {
        List<Tile> suggestions = new ArrayList<Tile>();
        final List<Tile> suggestions = new ArrayList<>();
        for (String pkgName : pkgNames) {
            suggestions.add(makeSuggestion(pkgName, "cls"));
        }
        return suggestions;
    }

    private Tile makeSuggestion(String pkgName, String className) {
            Tile suggestion = new Tile();
            suggestion.intent = new Intent("action");
        suggestion.intent.setComponent(new ComponentName(pkgName, className));
        return suggestion;
            suggestion.intent.setComponent(new ComponentName(pkgName, "cls"));
            suggestions.add(suggestion);
        }
        return suggestions;
    }

    private void setUpSuggestions(List<Tile> suggestions) {
        mDashboardAdapter.setCategoriesAndSuggestions(
                new ArrayList<DashboardCategory>(), suggestions);
        mDashboardAdapter.setCategoriesAndSuggestions(new ArrayList<>(), suggestions);
        mSuggestionHolder = mDashboardAdapter.onCreateViewHolder(
                new FrameLayout(RuntimeEnvironment.application),
                mDashboardAdapter.getItemViewType(0));
Loading