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

Commit e622690e authored by Soroosh Mariooryad's avatar Soroosh Mariooryad Committed by Android (Google) Code Review
Browse files

Merge "Modifying SuggestionParser to support dismiss logic of smart suggestions."

parents ad8cab38 56ce766e
Loading
Loading
Loading
Loading
+44 −13
Original line number Diff line number Diff line
@@ -94,16 +94,27 @@ public class SuggestionParser {

    private static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;

    // Default dismiss control for smart suggestions.
    private static final String DEFAULT_SMART_DISMISS_CONTROL = "0,10";

    private final Context mContext;
    private final List<SuggestionCategory> mSuggestionList;
    private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
    private final SharedPreferences mSharedPrefs;
    private final String mSmartDismissControl;

    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {

    public SuggestionParser(
        Context context, SharedPreferences sharedPrefs, int orderXml, String smartDismissControl) {
        mContext = context;
        mSuggestionList = (List<SuggestionCategory>) new SuggestionOrderInflater(mContext)
                .parse(orderXml);
        mSharedPrefs = sharedPrefs;
        mSmartDismissControl = smartDismissControl;
    }

    public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
       this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
    }

    @VisibleForTesting
@@ -111,26 +122,35 @@ public class SuggestionParser {
        mContext = context;
        mSuggestionList = new ArrayList<SuggestionCategory>();
        mSharedPrefs = sharedPrefs;
        mSmartDismissControl = DEFAULT_SMART_DISMISS_CONTROL;
        Log.wtf(TAG, "Only use this constructor for testing");
    }

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

    public List<Tile> getSuggestions(boolean isSmartSuggestionEnabled) {
        List<Tile> suggestions = new ArrayList<>();
        final int N = mSuggestionList.size();
        for (int i = 0; i < N; i++) {
            readSuggestions(mSuggestionList.get(i), suggestions);
            readSuggestions(mSuggestionList.get(i), suggestions, isSmartSuggestionEnabled);
        }
        return suggestions;
    }

    public boolean dismissSuggestion(Tile suggestion) {
        return dismissSuggestion(suggestion, false);
    }

    /**
     * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
     * be disabled.
     */
    public boolean dismissSuggestion(Tile suggestion) {
    public boolean dismissSuggestion(Tile suggestion, boolean isSmartSuggestionEnabled) {
        String keyBase = suggestion.intent.getComponent().flattenToShortString();
        int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
        String dismissControl = suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
        String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
        if (dismissControl == null || parseDismissString(dismissControl).length == index) {
            return true;
        }
@@ -141,20 +161,23 @@ public class SuggestionParser {
    }

    @VisibleForTesting
    public void filterSuggestions(List<Tile> suggestions, int countBefore) {
    public void filterSuggestions(
        List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
        for (int i = countBefore; i < suggestions.size(); i++) {
            if (!isAvailable(suggestions.get(i)) ||
                    !isSupported(suggestions.get(i)) ||
                    !satisifesRequiredUserType(suggestions.get(i)) ||
                    !satisfiesRequiredAccount(suggestions.get(i)) ||
                    !satisfiesConnectivity(suggestions.get(i)) ||
                    isDismissed(suggestions.get(i))) {
                    isDismissed(suggestions.get(i), isSmartSuggestionEnabled)) {
                suggestions.remove(i--);
            }
        }
    }

    private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
    @VisibleForTesting
    void readSuggestions(
        SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
        int countBefore = suggestions.size();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(category.category);
@@ -163,7 +186,7 @@ public class SuggestionParser {
        }
        TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
                mAddCache, null, suggestions, true, false);
        filterSuggestions(suggestions, countBefore);
        filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
        if (!category.multiple && suggestions.size() > (countBefore + 1)) {
            // If there are too many, remove them all and only re-add the one with the highest
            // priority.
@@ -288,12 +311,11 @@ public class SuggestionParser {
        Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
    }

    private boolean isDismissed(Tile suggestion) {
        Object dismissObj = suggestion.metaData.get(META_DATA_DISMISS_CONTROL);
        if (dismissObj == null) {
    private boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
        String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
        if (dismissControl == null) {
            return false;
        }
        String dismissControl = String.valueOf(dismissObj);
        String keyBase = suggestion.intent.getComponent().flattenToShortString();
        if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
            mSharedPrefs.edit()
@@ -333,7 +355,16 @@ public class SuggestionParser {
        return dismisses;
    }

    private static class SuggestionCategory {
    private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
        if (isSmartSuggestionEnabled) {
            return mSmartDismissControl;
        } else {
            return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
        }
    }

    @VisibleForTesting
    static class SuggestionCategory {
        public String category;
        public String pkg;
        public boolean multiple;
+6 −0
Original line number Diff line number Diff line
@@ -43,6 +43,12 @@ LOCAL_AAPT_FLAGS := --auto-add-overlay \
LOCAL_SRC_FILES := \
    $(call all-java-files-under, src)

LOCAL_JAR_EXCLUDE_FILES := none

LOCAL_RESOURCE_DIR := \
    $(LOCAL_PATH)/res


include frameworks/base/packages/SettingsLib/common.mk

include $(BUILD_PACKAGE)
+27 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->

<optional-steps>
    <step category="com.android.settings.suggested.category.LOCK_SCREEN" />
    <step category="com.android.settings.suggested.category.EMAIL" />
    <step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
        multiple="true" />
    <step category="com.android.settings.suggested.category.HOTWORD" />
    <step category="com.android.settings.suggested.category.DEFAULT"
        multiple="true" />
    <step category="com.android.settings.suggested.category.SETTINGS_ONLY"
        multiple="true" />
</optional-steps>
+48 −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;

import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.Fs;
import org.robolectric.res.ResourcePath;

import java.util.List;

public class SettingLibRobolectricTestRunner extends RobolectricTestRunner {

    public SettingLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
        super(testClass);
    }

    @Override
    protected AndroidManifest getAppManifest(Config config) {
        // Using the manifest file's relative path, we can figure out the application directory.
        final String appRoot = "frameworks/base/packages/SettingsLib";
        final String manifestPath = appRoot + "/AndroidManifest.xml";
        final String resDir = appRoot + "/tests/robotests/res";
        final String assetsDir = appRoot + config.assetDir();

        final AndroidManifest manifest = new AndroidManifest(Fs.fileFromPath(manifestPath),
                Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir));

        manifest.setPackageName("com.android.settingslib");
        return manifest;
    }

}
+132 −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;

import android.util.Log;
import android.content.Context;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.preference.PreferenceManager;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

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

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

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.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import org.robolectric.RuntimeEnvironment;

@RunWith(SettingLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SuggestionParserTest {

    @Mock
    private PackageManager mPackageManager;
    private Context mContext;
    private SuggestionParser mSuggestionParser;
    private SuggestionParser.SuggestionCategory mSuggestioCategory;
    private List<Tile> mSuggestionsBeforeDismiss;
    private List<Tile> mSuggestionsAfterDismiss;
    private SharedPreferences mPrefs;
    private Tile mSuggestion;
    private List<ResolveInfo> mInfo;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        when(mContext.getPackageManager()).thenReturn(mPackageManager);
        mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
        mSuggestion = new Tile();
        mSuggestion.intent = new Intent("action");
        mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
        mSuggestion.metaData = new Bundle();
        mSuggestionParser = new SuggestionParser(
            mContext, mPrefs, R.xml.suggestion_ordering, "0,0");
        mSuggestioCategory = new SuggestionParser.SuggestionCategory();
        mSuggestioCategory.category = "category1";
        mSuggestioCategory.multiple = true;
        mInfo = new ArrayList<>();
        ResolveInfo info1 = TileUtilsTest.newInfo(true, "category1");
        info1.activityInfo.packageName = "pkg";
        ResolveInfo info2 = TileUtilsTest.newInfo(true, "category1");
        info2.activityInfo.packageName = "pkg2";
        mInfo.add(info1);
        mInfo.add(info2);
        when(mPackageManager.queryIntentActivitiesAsUser(
            any(Intent.class), anyInt(), anyInt())).thenReturn(mInfo);
    }

    @Test
    public void testDismissSuggestion_withoutSmartSuggestion() {
        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, false)).isTrue();
    }

    @Test
    public void testDismissSuggestion_withSmartSuggestion() {
        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, true)).isFalse();
    }

    @Test
    public void testGetSuggestions_withoutSmartSuggestions() {
        readAndDismissSuggestion(false);
        mSuggestionParser.readSuggestions(mSuggestioCategory, mSuggestionsAfterDismiss, false);
        assertThat(mSuggestionsBeforeDismiss.size()).isEqualTo(2);
        assertThat(mSuggestionsAfterDismiss.size()).isEqualTo(1);
        assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
    }

    @Test
    public void testGetSuggestions_withSmartSuggestions() {
        readAndDismissSuggestion(true);
        assertThat(mSuggestionsBeforeDismiss.size()).isEqualTo(2);
        assertThat(mSuggestionsAfterDismiss.size()).isEqualTo(2);
        assertThat(mSuggestionsBeforeDismiss).isEqualTo(mSuggestionsAfterDismiss);
    }

    private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
        mSuggestionsBeforeDismiss = new ArrayList<Tile>();
        mSuggestionsAfterDismiss = new ArrayList<Tile>();
        mSuggestionParser.readSuggestions(
            mSuggestioCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
        if (mSuggestionParser.dismissSuggestion(
            mSuggestionsBeforeDismiss.get(0), isSmartSuggestionEnabled)) {
            mInfo.remove(0);
        }
        mSuggestionParser.readSuggestions(
            mSuggestioCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
    }
}
Loading