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

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

Merge "Add first impression type suggestion framework support"

parents 75db93ac a0f8d231
Loading
Loading
Loading
Loading
+63 −9
Original line number Diff line number Diff line
@@ -32,20 +32,25 @@ import android.os.UserManager;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.Xml;
import android.view.InflateException;

import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class SuggestionParser {

@@ -104,8 +109,8 @@ public class SuggestionParser {
    private final String mSmartDismissControl;


    public SuggestionParser(
        Context context, SharedPreferences sharedPrefs, int orderXml, String smartDismissControl) {
    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
            String smartDismissControl) {
        this(
                context,
                sharedPrefs,
@@ -138,19 +143,25 @@ public class SuggestionParser {
        final int N = mSuggestionList.size();
        for (int i = 0; i < N; i++) {
            final SuggestionCategory category = mSuggestionList.get(i);
            if (category.exclusive) {
            if (category.exclusive && !isExclusiveCategoryExpired(category)) {
                // If suggestions from an exclusive category are present, parsing is stopped
                // and only suggestions from that category are displayed. Note that subsequent
                // exclusive categories are also ignored.
                List<Tile> exclusiveSuggestions = new ArrayList<>();
                readSuggestions(category, exclusiveSuggestions, isSmartSuggestionEnabled);
                final List<Tile> exclusiveSuggestions = new ArrayList<>();

                // Read suggestion and force isSmartSuggestion to be false so the rule defined
                // from each suggestion itself is used.
                readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
                if (!exclusiveSuggestions.isEmpty()) {
                    return exclusiveSuggestions;
                }
            } else {
                // Either the category is not exclusive, or the exclusiveness expired so we should
                // treat it as a normal category.
                readSuggestions(category, suggestions, isSmartSuggestionEnabled);
            }
        }
        dedupeSuggestions(suggestions);
        return suggestions;
    }

@@ -190,6 +201,22 @@ 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) {
@@ -326,6 +353,25 @@ public class SuggestionParser {
        Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
    }

    /**
     * Whether or not the category's exclusiveness has expired.
     */
    private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
        final String keySetupTime = category.category + SETUP_TIME;
        final long currentTime = System.currentTimeMillis();
        if (!mSharedPrefs.contains(keySetupTime)) {
            mSharedPrefs.edit()
                    .putLong(keySetupTime, currentTime)
                    .commit();
        }
        if (category.exclusiveExpireDaysInMillis < 0) {
            // negative means never expires
            return false;
        }
        final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
        return currentTime - setupTime > category.exclusiveExpireDaysInMillis;
    }

    private boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
        String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
        if (dismissControl == null) {
@@ -384,6 +430,7 @@ public class SuggestionParser {
        public String pkg;
        public boolean multiple;
        public boolean exclusive;
        public long exclusiveExpireDaysInMillis;
    }

    private static class SuggestionOrderInflater {
@@ -394,6 +441,7 @@ public class SuggestionParser {
        private static final String ATTR_PACKAGE = "package";
        private static final String ATTR_MULTIPLE = "multiple";
        private static final String ATTR_EXCLUSIVE = "exclusive";
        private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";

        private final Context mContext;

@@ -471,6 +519,12 @@ public class SuggestionParser {
                String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
                category.exclusive =
                        !TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
                String expireDaysAttr = attrs.getAttributeValue(null,
                        ATTR_EXCLUSIVE_EXPIRE_DAYS);
                long expireDays = !TextUtils.isEmpty(expireDaysAttr)
                        ? Integer.parseInt(expireDaysAttr)
                        : -1;
                category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
                return category;
            } else {
                throw new IllegalArgumentException("Unknown item " + name);
+54 −9
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.settingslib;

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

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -36,19 +34,24 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.res.ResourceLoader;
import org.robolectric.res.builder.DefaultPackageManager;
import org.robolectric.res.builder.RobolectricPackageManager;

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

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

@RunWith(SettingLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
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 List<Tile> mSuggestionsBeforeDismiss;
    private List<Tile> mSuggestionsAfterDismiss;
    private SharedPreferences mPrefs;
@@ -59,6 +62,7 @@ public class SuggestionParserTest {
        RuntimeEnvironment.setRobolectricPackageManager(
                new TestPackageManager(RuntimeEnvironment.getAppResourceLoader()));
        mContext = RuntimeEnvironment.application;
        mPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
        mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
        mSuggestion = new Tile();
        mSuggestion.intent = new Intent("action");
@@ -70,21 +74,36 @@ public class SuggestionParserTest {
        mExclusiveCategory = new SuggestionParser.SuggestionCategory();
        mExclusiveCategory.category = "category2";
        mExclusiveCategory.exclusive = true;
        mSuggestionParser = new SuggestionParser(
                mContext, mPrefs, Arrays.asList(mMultipleCategory, mExclusiveCategory), "0,0");
        mExpiredExclusiveCategory = new SuggestionParser.SuggestionCategory();
        mExpiredExclusiveCategory.category = "category3";
        mExpiredExclusiveCategory.exclusive = true;
        mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;

        mSuggestionParser = new SuggestionParser(mContext, mPrefs,
                Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
                "0,0");

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

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

        Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
        Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
        RuntimeEnvironment.getRobolectricPackageManager().addResolveInfoForIntent(intent1, info1);
        RuntimeEnvironment.getRobolectricPackageManager().addResolveInfoForIntent(intent1, info2);
        RuntimeEnvironment.getRobolectricPackageManager().addResolveInfoForIntent(intent2, info3);
        Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");

        mPackageManager.addResolveInfoForIntent(intent1, info1);
        mPackageManager.addResolveInfoForIntent(intent1, info2);
        mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
        mPackageManager.addResolveInfoForIntent(intent2, info3);
        mPackageManager.addResolveInfoForIntent(intent3, info4);
    }

    @Test
@@ -115,16 +134,42 @@ public class SuggestionParserTest {
    }

    @Test
    public void testGetSuggestion_exclusiveNotAvailable() {
        RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
    public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
        mPackageManager.removeResolveInfosForIntent(
                new Intent(Intent.ACTION_MAIN).addCategory("category2"),
                "pkg3");
        mPackageManager.removeResolveInfosForIntent(
                new Intent(Intent.ACTION_MAIN).addCategory("category3"),
                "pkg4");

        // If exclusive item is not available, the other categories should be shown
        final List<Tile> suggestions = mSuggestionParser.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");
    }

    @Test
    public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
        // First remove permanent exclusive
        mPackageManager.removeResolveInfosForIntent(
                new Intent(Intent.ACTION_MAIN).addCategory("category2"),
                "pkg3");
        // Set the other exclusive to be expired.
        mPrefs.edit()
                .putLong(mExpiredExclusiveCategory.category + "_setup_time",
                        System.currentTimeMillis() - 1000)
                .commit();

        // If exclusive is expired, they should be shown together with the other categories
        final List<Tile> suggestions = mSuggestionParser.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");
    }

    @Test