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

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

Refactor suggestion parser.

Move it to a suggestions package
Wrap suggestion list with a SuggestionList class, with additional
metadata.

Bug: 37947647
Test: make RunSettingsLibRoboTests
Change-Id: I1343d76c8e9acc0d17eb00a87e7f3a86551481d8
parent 5bf85567
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
/*
 * 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.settingslib.suggestions;

public class SuggestionCategory {
    public String category;
    public String pkg;
    public boolean multiple;
    public boolean exclusive;
    public long exclusiveExpireDaysInMillis;
}
+95 −0
Original line number Diff line number Diff line
/*
 * 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.settingslib.suggestions;

import android.content.Intent;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.settingslib.drawer.Tile;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SuggestionList {
    // Category -> list of suggestion map
    private final Map<SuggestionCategory, List<Tile>> mSuggestions;

    // A flatten list of all suggestions.
    private List<Tile> mSuggestionList;

    public SuggestionList() {
        mSuggestions = new ArrayMap<>();
    }

    public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
        mSuggestions.put(category, suggestions);
    }

    public List<Tile> getSuggestions() {
        if (mSuggestionList != null) {
            return mSuggestionList;
        }
        mSuggestionList = new ArrayList<>();
        for (List<Tile> suggestions : mSuggestions.values()) {
            mSuggestionList.addAll(suggestions);
        }
        dedupeSuggestions(mSuggestionList);
        return mSuggestionList;
    }

    public boolean isExclusiveSuggestionCategory() {
        if (mSuggestions.size() != 1) {
            // If there is no category, or more than 1 category, it's not exclusive by definition.
            return false;
        }
        for (SuggestionCategory category : mSuggestions.keySet()) {
            if (category.exclusive) {
                return true;
            }
        }
        return false;
    }

    public List<Tile> getSuggestionForCategory(String category) {
        for (Map.Entry<SuggestionCategory, List<Tile>> entry : mSuggestions.entrySet()) {
            if (TextUtils.equals(entry.getKey().category, category)) {
                return entry.getValue();
            }
        }
        return null;
    }

    /**
     * Filter suggestions list so they are all unique.
     */
    private void dedupeSuggestions(List<Tile> suggestions) {
        final Set<String> intents = new ArraySet<>();
        for (int i = suggestions.size() - 1; i >= 0; i--) {
            final Tile suggestion = suggestions.get(i);
            final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
            if (intents.contains(intentUri)) {
                suggestions.remove(i);
            } else {
                intents.add(intentUri);
            }
        }
    }
}
+11 −37
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 * 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.
@@ -11,9 +11,9 @@
 * 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
 * limitations under the License.
 */
package com.android.settingslib;
package com.android.settingslib.suggestions;

import android.Manifest;
import android.accounts.Account;
@@ -134,12 +134,8 @@ public class SuggestionParser {
        mSmartDismissControl = smartDismissControl;
    }

    public List<Tile> getSuggestions() {
        return getSuggestions(false);
    }

    public List<Tile> getSuggestions(boolean isSmartSuggestionEnabled) {
        List<Tile> suggestions = new ArrayList<>();
    public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
        final SuggestionList suggestionList = new SuggestionList();
        final int N = mSuggestionList.size();
        for (int i = 0; i < N; i++) {
            final SuggestionCategory category = mSuggestionList.get(i);
@@ -153,16 +149,19 @@ public class SuggestionParser {
                // from each suggestion itself is used.
                readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
                if (!exclusiveSuggestions.isEmpty()) {
                    return exclusiveSuggestions;
                    final SuggestionList exclusiveList = new SuggestionList();
                    exclusiveList.addSuggestions(category, exclusiveSuggestions);
                    return exclusiveList;
                }
            } else {
                // Either the category is not exclusive, or the exclusiveness expired so we should
                // treat it as a normal category.
                final List<Tile> suggestions = new ArrayList<>();
                readSuggestions(category, suggestions, isSmartSuggestionEnabled);
                suggestionList.addSuggestions(category, suggestions);
            }
        }
        dedupeSuggestions(suggestions);
        return suggestions;
        return suggestionList;
    }

    public boolean dismissSuggestion(Tile suggestion) {
@@ -201,22 +200,6 @@ public class SuggestionParser {
        }
    }

    /**
     * Filter suggestions list so they are all unique.
     */
    private void dedupeSuggestions(List<Tile> suggestions) {
        final Set<String> intents = new ArraySet<>();
        for (int i = suggestions.size() - 1; i >= 0; i--) {
            final Tile suggestion = suggestions.get(i);
            final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
            if (intents.contains(intentUri)) {
                suggestions.remove(i);
            } else {
                intents.add(intentUri);
            }
        }
    }

    @VisibleForTesting
    void readSuggestions(
            SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
@@ -424,15 +407,6 @@ public class SuggestionParser {
        }
    }

    @VisibleForTesting
    static class SuggestionCategory {
        public String category;
        public String pkg;
        public boolean multiple;
        public boolean exclusive;
        public long exclusiveExpireDaysInMillis;
    }

    private static class SuggestionOrderInflater {
        private static final String TAG_LIST = "optional-steps";
        private static final String TAG_ITEM = "step";
+18 −19
Original line number Diff line number Diff line
@@ -16,18 +16,6 @@

package com.android.settingslib.drawer;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -50,8 +38,8 @@ import android.util.ArrayMap;
import android.util.Pair;

import com.android.settingslib.R;
import com.android.settingslib.SuggestionParser;
import com.android.settingslib.TestConfig;
import com.android.settingslib.suggestions.SuggestionParser;

import org.junit.Before;
import org.junit.Test;
@@ -69,6 +57,17 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+35 −23
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

package com.android.settingslib;
package com.android.settingslib.suggestions;

import android.content.ComponentName;
import android.content.Context;
@@ -24,6 +24,8 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;

import com.android.settingslib.SettingLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtilsTest;

@@ -49,9 +51,9 @@ public class SuggestionParserTest {
    private Context mContext;
    private RobolectricPackageManager mPackageManager;
    private SuggestionParser mSuggestionParser;
    private SuggestionParser.SuggestionCategory mMultipleCategory;
    private SuggestionParser.SuggestionCategory mExclusiveCategory;
    private SuggestionParser.SuggestionCategory mExpiredExclusiveCategory;
    private SuggestionCategory mMultipleCategory;
    private SuggestionCategory mExclusiveCategory;
    private SuggestionCategory mExpiredExclusiveCategory;
    private List<Tile> mSuggestionsBeforeDismiss;
    private List<Tile> mSuggestionsAfterDismiss;
    private SharedPreferences mPrefs;
@@ -68,13 +70,13 @@ public class SuggestionParserTest {
        mSuggestion.intent = new Intent("action");
        mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
        mSuggestion.metaData = new Bundle();
        mMultipleCategory = new SuggestionParser.SuggestionCategory();
        mMultipleCategory = new SuggestionCategory();
        mMultipleCategory.category = "category1";
        mMultipleCategory.multiple = true;
        mExclusiveCategory = new SuggestionParser.SuggestionCategory();
        mExclusiveCategory = new SuggestionCategory();
        mExclusiveCategory.category = "category2";
        mExclusiveCategory.exclusive = true;
        mExpiredExclusiveCategory = new SuggestionParser.SuggestionCategory();
        mExpiredExclusiveCategory = new SuggestionCategory();
        mExpiredExclusiveCategory.category = "category3";
        mExpiredExclusiveCategory.exclusive = true;
        mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
@@ -83,16 +85,16 @@ public class SuggestionParserTest {
                Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
                "0,0");

        ResolveInfo info1 = TileUtilsTest.newInfo(true, "category1");
        ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
        info1.activityInfo.packageName = "pkg";
        ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, "category1");
        ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
        infoDupe1.activityInfo.packageName = "pkg";

        ResolveInfo info2 = TileUtilsTest.newInfo(true, "category1");
        ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
        info2.activityInfo.packageName = "pkg2";
        ResolveInfo info3 = TileUtilsTest.newInfo(true, "category2");
        ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
        info3.activityInfo.packageName = "pkg3";
        ResolveInfo info4 = TileUtilsTest.newInfo(true, "category3");
        ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
        info4.activityInfo.packageName = "pkg4";

        Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
@@ -143,12 +145,12 @@ public class SuggestionParserTest {
                "pkg4");

        // If exclusive item is not available, the other categories should be shown
        final List<Tile> suggestions = mSuggestionParser.getSuggestions();

        final SuggestionList sl =
                mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
        final List<Tile> suggestions = sl.getSuggestions();
        assertThat(suggestions).hasSize(2);
        assertThat(suggestions.get(0).category).isEqualTo("category1");

        assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
        assertThat(suggestions.get(1).category).isEqualTo("category1");
        assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
    }

@@ -165,18 +167,27 @@ public class SuggestionParserTest {
                .commit();

        // If exclusive is expired, they should be shown together with the other categories
        final List<Tile> suggestions = mSuggestionParser.getSuggestions();
        final SuggestionList sl =
                mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
        final List<Tile> suggestions = sl.getSuggestions();

        assertThat(suggestions).hasSize(3);
        assertThat(suggestions.get(0).category).isEqualTo("category1");
        assertThat(suggestions.get(1).category).isEqualTo("category1");
        assertThat(suggestions.get(2).category).isEqualTo("category3");

        final List<Tile> category1Suggestions = sl.getSuggestionForCategory("category1");
        final List<Tile> category3Suggestions = sl.getSuggestionForCategory("category3");

        assertThat(category1Suggestions).hasSize(2);
        assertThat(category3Suggestions).hasSize(1);
    }

    @Test
    public void testGetSuggestions_exclusive() {
        final List<Tile> suggestions = mSuggestionParser.getSuggestions();
        final SuggestionList sl =
                mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
        final List<Tile> suggestions = sl.getSuggestions();

        assertThat(suggestions).hasSize(1);
        assertThat(suggestions.get(0).category).isEqualTo("category2");
        assertThat(sl.getSuggestionForCategory("category2")).hasSize(1);
    }

    private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
@@ -184,10 +195,11 @@ public class SuggestionParserTest {
        mSuggestionsAfterDismiss = new ArrayList<>();
        mSuggestionParser.readSuggestions(
                mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);

        final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
        if (mSuggestionParser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) {
            RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
                    new Intent(Intent.ACTION_MAIN).addCategory(suggestion.category),
                    new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
                    suggestion.intent.getComponent().getPackageName());
        }
        mSuggestionParser.readSuggestions(