From 78e097f239d967ae0bab6a087ac92bbf9d341799 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Mon, 8 Apr 2019 20:30:02 +0800 Subject: [PATCH 1/2] Hide notification channel slice that is interacted - Ignore interacted package in ContextualNotificationChannelSlice. - Regularly clear package record with background worker. Bug:129726858 Test: visual, robotests Change-Id: I94661a53bcbbe4a15479224c33cfb2eff345aa67 --- .../ContextualNotificationChannelSlice.java | 23 ++++ .../slices/NotificationChannelSlice.java | 16 ++- .../slices/NotificationChannelWorker.java | 77 ++++++++++++ ...ontextualNotificationChannelSliceTest.java | 49 ++++++++ .../slices/NotificationChannelSliceTest.java | 16 ++- .../slices/NotificationChannelWorkerTest.java | 112 ++++++++++++++++++ 6 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorker.java create mode 100644 tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorkerTest.java diff --git a/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java index 2025a06a71a..17cae777b75 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSlice.java @@ -16,14 +16,23 @@ package com.android.settings.homepage.contextualcards.slices; +import static android.content.Context.MODE_PRIVATE; + import android.content.Context; import android.net.Uri; +import android.util.ArraySet; import com.android.settings.R; import com.android.settings.slices.CustomSliceRegistry; +import com.android.settings.slices.SliceBackgroundWorker; + +import java.util.Set; public class ContextualNotificationChannelSlice extends NotificationChannelSlice { + public static final String PREFS = "notification_channel_slice_prefs"; + public static final String PREF_KEY_INTERACTED_PACKAGES = "interacted_packages"; + public ContextualNotificationChannelSlice(Context context) { super(context); } @@ -37,4 +46,18 @@ public class ContextualNotificationChannelSlice extends NotificationChannelSlice protected CharSequence getSubTitle(String packageName, int uid) { return mContext.getText(R.string.recently_installed_app); } + + @Override + protected boolean isUserInteracted(String packageName) { + // Check the package has been interacted on current slice or not. + final Set interactedPackages = + mContext.getSharedPreferences(PREFS, MODE_PRIVATE) + .getStringSet(PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + return interactedPackages.contains(packageName); + } + + @Override + public Class getBackgroundWorkerClass() { + return NotificationChannelWorker.class; + } } diff --git a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java index 0550e7b30eb..e5cee37abcf 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSlice.java @@ -218,6 +218,17 @@ public class NotificationChannelSlice implements CustomSliceable { .toIntent(); } + /** + * Check the package has been interacted by user or not. + * Will use to filter package in {@link #getRecentlyInstalledPackages()}. + * + * @param packageName The app package name. + * @return true if the package was interacted, false otherwise. + */ + protected boolean isUserInteracted(String packageName) { + return false; + } + @VisibleForTesting IconCompat getApplicationIcon(String packageName) { final Drawable drawable; @@ -328,8 +339,9 @@ public class NotificationChannelSlice implements CustomSliceable { final List installedPackages = mContext.getPackageManager().getInstalledPackages(0); for (PackageInfo packageInfo : installedPackages) { - // Not include system app. - if (packageInfo.applicationInfo.isSystemApp()) { + // Not include system app and interacted app. + if (packageInfo.applicationInfo.isSystemApp() + || isUserInteracted(packageInfo.packageName)) { continue; } diff --git a/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorker.java b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorker.java new file mode 100644 index 00000000000..f1d0d593cfa --- /dev/null +++ b/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorker.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 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.settings.homepage.contextualcards.slices; + +import static android.content.Context.MODE_PRIVATE; + +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREFS; +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.net.Uri; +import android.util.ArraySet; + +import com.android.settings.slices.SliceBackgroundWorker; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class NotificationChannelWorker extends SliceBackgroundWorker { + + public NotificationChannelWorker(Context context, Uri uri) { + super(context, uri); + } + + @Override + protected void onSlicePinned() { + } + + @Override + protected void onSliceUnpinned() { + removeUninstalledPackages(); + } + + @Override + public void close() throws IOException { + } + + private void removeUninstalledPackages() { + final SharedPreferences prefs = getContext().getSharedPreferences(PREFS, MODE_PRIVATE); + final Set interactedPackages = + prefs.getStringSet(PREF_KEY_INTERACTED_PACKAGES, new ArraySet()); + if (interactedPackages.isEmpty()) { + return; + } + + final List installedPackageInfos = + getContext().getPackageManager().getInstalledPackages(0); + final List installedPackages = installedPackageInfos.stream() + .map(packageInfo -> packageInfo.packageName) + .collect(Collectors.toList()); + final Set newInteractedPackages = new ArraySet<>(); + for (String packageName : interactedPackages) { + if (installedPackages.contains(packageName)) { + newInteractedPackages.add(packageName); + } + } + prefs.edit().putStringSet(PREF_KEY_INTERACTED_PACKAGES, newInteractedPackages).apply(); + } +} diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java index f2b87be53ed..8541a30c9a9 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/ContextualNotificationChannelSliceTest.java @@ -16,30 +16,49 @@ package com.android.settings.homepage.contextualcards.slices; +import static android.content.Context.MODE_PRIVATE; + +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREFS; +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES; + import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.content.SharedPreferences; import android.net.Uri; +import android.util.ArraySet; import com.android.settings.R; import com.android.settings.slices.CustomSliceRegistry; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) public class ContextualNotificationChannelSliceTest { + private static final String PACKAGE_NAME = "package_name"; + private Context mContext; private ContextualNotificationChannelSlice mNotificationChannelSlice; + private SharedPreferences mSharedPreferences; @Before public void setUp() { mContext = RuntimeEnvironment.application; mNotificationChannelSlice = new ContextualNotificationChannelSlice(mContext); + mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE); + } + + @After + public void tearDown() { + removeInteractedPackageFromSharedPreference(); } @Test @@ -55,4 +74,34 @@ public class ContextualNotificationChannelSliceTest { assertThat(subTitle).isEqualTo(mContext.getText(R.string.recently_installed_app)); } + + @Test + public void isUserInteracted_hasInteractedPackage_shouldBeTrue() { + addInteractedPackageToSharedPreference(); + + final boolean isInteracted = mNotificationChannelSlice.isUserInteracted(PACKAGE_NAME); + + assertThat(isInteracted).isTrue(); + } + + @Test + public void isUserInteracted_noInteractedPackage_shouldBeFalse() { + final boolean isInteracted = mNotificationChannelSlice.isUserInteracted(PACKAGE_NAME); + + assertThat(isInteracted).isFalse(); + } + + private void addInteractedPackageToSharedPreference() { + final Set interactedPackages = new ArraySet<>(); + interactedPackages.add(PACKAGE_NAME); + + mSharedPreferences.edit().putStringSet(PREF_KEY_INTERACTED_PACKAGES, + interactedPackages).apply(); + } + + private void removeInteractedPackageFromSharedPreference() { + if (mSharedPreferences.contains(PREF_KEY_INTERACTED_PACKAGES)) { + mSharedPreferences.edit().remove(PREF_KEY_INTERACTED_PACKAGES).apply(); + } + } } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSliceTest.java index 12513f67e1a..81f5797c659 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelSliceTest.java @@ -26,7 +26,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.NotificationChannel; @@ -299,6 +298,21 @@ public class NotificationChannelSliceTest { assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.no_suggested_app)); } + @Test + @Config(shadows = ShadowRestrictedLockUtilsInternal.class) + public void getSlice_isInteractedPackage_shouldHaveNoSuggestedAppTitle() { + addMockPackageToPackageManager(true /* isRecentlyInstalled */, + ApplicationInfo.FLAG_INSTALLED); + mockNotificationBackend(CHANNEL_COUNT, NOTIFICATION_COUNT, false /* banned */, + false /* isChannelBlocked */); + doReturn(true).when(mNotificationChannelSlice).isUserInteracted(any(String.class)); + + final Slice slice = mNotificationChannelSlice.getSlice(); + + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.no_suggested_app)); + } + private void addMockPackageToPackageManager(boolean isRecentlyInstalled, int flags) { final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.name = APP_LABEL; diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorkerTest.java new file mode 100644 index 00000000000..6ac8b708262 --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/NotificationChannelWorkerTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 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.settings.homepage.contextualcards.slices; + +import static android.content.Context.MODE_PRIVATE; + +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREFS; +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES; + +import static com.google.common.truth.Truth.assertThat; + +import static org.robolectric.Shadows.shadowOf; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.net.Uri; +import android.util.ArraySet; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowPackageManager; + +import java.util.Set; + +@RunWith(RobolectricTestRunner.class) +public class NotificationChannelWorkerTest { + private static final Uri URI = Uri.parse("content://com.android.settings.slices/test"); + private static final String PACKAGE_NAME = "com.test.notification.channel.slice"; + + private Context mContext; + private NotificationChannelWorker mNotificationChannelWorker; + private ShadowPackageManager mPackageManager; + private SharedPreferences mSharedPreferences; + + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mNotificationChannelWorker = new NotificationChannelWorker(mContext, URI); + + // Shadow PackageManager to add mock package. + mPackageManager = shadowOf(mContext.getPackageManager()); + + mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE); + addInteractedPackageToSharedPreference(); + } + + @After + public void tearDown() { + mPackageManager.removePackage(PACKAGE_NAME); + removeInteractedPackageFromSharedPreference(); + } + + @Test + public void onSliceUnpinned_interactedPackageIsUninstalled_shouldRemovePackage() { + mNotificationChannelWorker.onSliceUnpinned(); + + final Set interactedPackages = mSharedPreferences.getStringSet( + PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + assertThat(interactedPackages.contains(PACKAGE_NAME)).isFalse(); + } + + @Test + public void onSliceUnpinned_interactedPackageIsInstalled_shouldKeepPackage() { + mockInteractedPackageAsInstalled(); + + mNotificationChannelWorker.onSliceUnpinned(); + + final Set interactedPackages = mSharedPreferences.getStringSet( + PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + assertThat(interactedPackages.contains(PACKAGE_NAME)).isTrue(); + } + + private void mockInteractedPackageAsInstalled() { + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = PACKAGE_NAME; + mPackageManager.addPackage(packageInfo); + } + + private void addInteractedPackageToSharedPreference() { + final Set interactedPackages = new ArraySet<>(); + interactedPackages.add(PACKAGE_NAME); + + mSharedPreferences.edit().putStringSet(PREF_KEY_INTERACTED_PACKAGES, + interactedPackages).apply(); + } + + private void removeInteractedPackageFromSharedPreference() { + if (mSharedPreferences.contains(PREF_KEY_INTERACTED_PACKAGES)) { + mSharedPreferences.edit().remove(PREF_KEY_INTERACTED_PACKAGES).apply(); + } + } +} From b29e53a4504b27b807c726c4099b6a9e69ccdc97 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Fri, 29 Mar 2019 00:38:56 +0800 Subject: [PATCH 2/2] Log interacted package from ContextualNotificationChannelSlice Fixes:129726858 Test: visual, robotests Change-Id: Ife4043fe0bcb52445e8e2efec20781ce43c54fef --- .../ContextualCardFeatureProvider.java | 5 +- .../ContextualCardFeatureProviderImpl.java | 38 +++++ .../slices/SliceFullCardRendererHelper.java | 7 + ...ContextualCardFeatureProviderImplTest.java | 130 ++++++++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java index bba7f533236..bdf863e3385 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java @@ -16,8 +16,11 @@ package com.android.settings.homepage.contextualcards; -import java.util.List; +import androidx.slice.Slice; /** Feature provider for the contextual card feature. */ public interface ContextualCardFeatureProvider { + + /** Log package when user clicks contextual notification channel card. */ + void logNotificationPackage(Slice slice); } diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java index 03a1550cebf..4af2838baa9 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java @@ -16,7 +16,22 @@ package com.android.settings.homepage.contextualcards; +import static android.content.Context.MODE_PRIVATE; + import android.content.Context; +import android.content.SharedPreferences; +import android.util.ArraySet; + +import androidx.slice.Slice; +import androidx.slice.SliceMetadata; +import androidx.slice.core.SliceAction; + +import com.android.settings.SettingsActivity; +import com.android.settings.applications.AppInfoBase; +import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice; +import com.android.settings.slices.CustomSliceRegistry; + +import java.util.Set; public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider { private final Context mContext; @@ -24,4 +39,27 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP public ContextualCardFeatureProviderImpl(Context context) { mContext = context; } + + @Override + public void logNotificationPackage(Slice slice) { + if (slice == null || !slice.getUri().equals( + CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI)) { + return; + } + + final SliceAction primaryAction = SliceMetadata.from(mContext, slice).getPrimaryAction(); + final String currentPackage = primaryAction.getAction().getIntent() + .getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) + .getString(AppInfoBase.ARG_PACKAGE_NAME); + + final SharedPreferences prefs = mContext.getSharedPreferences( + ContextualNotificationChannelSlice.PREFS, MODE_PRIVATE); + final Set interactedPackages = prefs.getStringSet( + ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + + final Set newInteractedPackages = new ArraySet<>(interactedPackages); + newInteractedPackages.add(currentPackage); + prefs.edit().putStringSet(ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES, + newInteractedPackages).apply(); + } } diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java index a9a83466496..f7b2bf5504f 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceFullCardRendererHelper.java @@ -27,6 +27,7 @@ import androidx.slice.widget.SliceView; import com.android.settings.R; import com.android.settings.homepage.contextualcards.ContextualCard; +import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider; import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -64,6 +65,12 @@ class SliceFullCardRendererHelper { metricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log); + + final ContextualCardFeatureProvider contextualCardFeatureProvider = + FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider( + mContext); + + contextualCardFeatureProvider.logNotificationPackage(slice); }); // Customize slice view for Settings diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java new file mode 100644 index 00000000000..e380636774a --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImplTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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.settings.homepage.contextualcards; + +import static android.content.Context.MODE_PRIVATE; + +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREFS; +import static com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES; +import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI; +import static com.android.settings.slices.CustomSliceRegistry.FLASHLIGHT_SLICE_URI; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.Bundle; +import android.util.ArraySet; + +import androidx.core.graphics.drawable.IconCompat; +import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; +import androidx.slice.widget.SliceLiveData; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.applications.AppInfoBase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.Set; + +@RunWith(RobolectricTestRunner.class) +public class ContextualCardFeatureProviderImplTest { + + private Context mContext; + private ContextualCardFeatureProviderImpl mImpl; + private SharedPreferences mSharedPreferences; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mImpl = new ContextualCardFeatureProviderImpl(mContext); + mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE); + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @After + public void tearDown() { + removeInteractedPackageFromSharedPreference(); + } + + @Test + public void logNotificationPackage_isContextualNotificationChannel_shouldLogPackage() { + final String packageName = "com.android.test.app"; + final Slice slice = buildSlice(CONTEXTUAL_NOTIFICATION_CHANNEL_SLICE_URI, packageName); + + mImpl.logNotificationPackage(slice); + + final Set interactedPackages = mSharedPreferences.getStringSet( + PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + assertThat(interactedPackages.contains(packageName)).isTrue(); + } + + @Test + public void logNotificationPackage_isNotContextualNotificationChannel_shouldNotLogPackage() { + final String packageName = "com.android.test.app"; + final Slice slice = buildSlice(FLASHLIGHT_SLICE_URI, packageName); + + mImpl.logNotificationPackage(slice); + + final Set interactedPackages = mSharedPreferences.getStringSet( + PREF_KEY_INTERACTED_PACKAGES, new ArraySet<>()); + assertThat(interactedPackages.contains(packageName)).isFalse(); + } + + private Slice buildSlice(Uri sliceUri, String packageName) { + final Bundle args = new Bundle(); + args.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName); + final Intent intent = new Intent("action"); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); + + final PendingIntent pendingIntent = spy( + PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */)); + doReturn(intent).when(pendingIntent).getIntent(); + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.empty_icon); + final SliceAction action = SliceAction.createDeeplink(pendingIntent, icon, + ListBuilder.SMALL_IMAGE, "title"); + + return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY) + .addRow(new ListBuilder.RowBuilder() + .addEndItem(icon, ListBuilder.ICON_IMAGE) + .setTitle("title") + .setPrimaryAction(action)) + .build(); + } + + private void removeInteractedPackageFromSharedPreference() { + if (mSharedPreferences.contains(PREF_KEY_INTERACTED_PACKAGES)) { + mSharedPreferences.edit().remove(PREF_KEY_INTERACTED_PACKAGES).apply(); + } + } +}