Merge "Implement new dismissal behaviour of contextual card"

This commit is contained in:
TreeHugger Robot
2020-02-20 12:05:36 +00:00
committed by Android (Google) Code Review
10 changed files with 193 additions and 105 deletions

View File

@@ -48,7 +48,7 @@ public class CardContentProvider extends ContentProvider {
public static final Uri DELETE_CARD_URI = new Uri.Builder() public static final Uri DELETE_CARD_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT) .scheme(ContentResolver.SCHEME_CONTENT)
.authority(CardContentProvider.CARD_AUTHORITY) .authority(CardContentProvider.CARD_AUTHORITY)
.appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED) .appendPath(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP)
.build(); .build();
private static final String TAG = "CardContentProvider"; private static final String TAG = "CardContentProvider";

View File

@@ -16,9 +16,7 @@
package com.android.settings.homepage.contextualcards; package com.android.settings.homepage.contextualcards;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; import android.util.Log;
@@ -31,7 +29,7 @@ import androidx.annotation.VisibleForTesting;
public class CardDatabaseHelper extends SQLiteOpenHelper { public class CardDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "CardDatabaseHelper"; private static final String TAG = "CardDatabaseHelper";
private static final String DATABASE_NAME = "homepage_cards.db"; private static final String DATABASE_NAME = "homepage_cards.db";
private static final int DATABASE_VERSION = 6; private static final int DATABASE_VERSION = 7;
public static final String CARD_TABLE = "cards"; public static final String CARD_TABLE = "cards";
@@ -72,31 +70,32 @@ public class CardDatabaseHelper extends SQLiteOpenHelper {
String APP_VERSION = "app_version"; String APP_VERSION = "app_version";
/** /**
* Decide the card is dismissed or not. * Timestamp of card being dismissed.
*/ */
String CARD_DISMISSED = "card_dismissed"; String DISMISSED_TIMESTAMP = "dismissed_timestamp";
} }
private static final String CREATE_CARD_TABLE = private static final String CREATE_CARD_TABLE =
"CREATE TABLE " + CARD_TABLE + "CREATE TABLE "
"(" + + CARD_TABLE
CardColumns.NAME + + "("
" TEXT NOT NULL PRIMARY KEY, " + + CardColumns.NAME
CardColumns.TYPE + + " TEXT NOT NULL PRIMARY KEY, "
" INTEGER NOT NULL, " + + CardColumns.TYPE
CardColumns.SCORE + + " INTEGER NOT NULL, "
" DOUBLE NOT NULL, " + + CardColumns.SCORE
CardColumns.SLICE_URI + + " DOUBLE NOT NULL, "
" TEXT, " + + CardColumns.SLICE_URI
CardColumns.CATEGORY + + " TEXT, "
" INTEGER DEFAULT 0, " + + CardColumns.CATEGORY
CardColumns.PACKAGE_NAME + + " INTEGER DEFAULT 0, "
" TEXT NOT NULL, " + + CardColumns.PACKAGE_NAME
CardColumns.APP_VERSION + + " TEXT NOT NULL, "
" INTEGER NOT NULL, " + + CardColumns.APP_VERSION
CardColumns.CARD_DISMISSED + + " INTEGER NOT NULL, "
" INTEGER DEFAULT 0 " + + CardColumns.DISMISSED_TIMESTAMP
");"; + " INTEGER"
+ ");";
public CardDatabaseHelper(Context context) { public CardDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION); super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -125,32 +124,4 @@ public class CardDatabaseHelper extends SQLiteOpenHelper {
} }
return sCardDatabaseHelper; return sCardDatabaseHelper;
} }
Cursor getContextualCards() {
final SQLiteDatabase db = getReadableDatabase();
final String selection = CardColumns.CARD_DISMISSED + "=0";
return db.query(CARD_TABLE, null /* columns */, selection,
null /* selectionArgs */, null /* groupBy */, null /* having */,
CardColumns.SCORE + " DESC" /* orderBy */);
}
/**
* Mark a specific ContextualCard with dismissal flag in the database to indicate that the
* card has been dismissed.
*
* @param context Context
* @param cardName The card name of the ContextualCard which is dismissed by user.
* @return The number of rows updated
*/
public int markContextualCardAsDismissed(Context context, String cardName) {
final SQLiteDatabase database = getWritableDatabase();
final ContentValues values = new ContentValues();
values.put(CardColumns.CARD_DISMISSED, 1);
final String selection = CardColumns.NAME + "=?";
final String[] selectionArgs = {cardName};
final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
database.close();
context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null);
return rowsUpdated;
}
} }

View File

@@ -16,10 +16,25 @@
package com.android.settings.homepage.contextualcards; package com.android.settings.homepage.contextualcards;
import android.content.Context;
import android.database.Cursor;
import androidx.slice.Slice; import androidx.slice.Slice;
/** Feature provider for the contextual card feature. */ /** Feature provider for the contextual card feature. */
public interface ContextualCardFeatureProvider { public interface ContextualCardFeatureProvider {
/** Get contextual cards from the card provider */
Cursor getContextualCards();
/**
* Mark a specific {@link ContextualCard} as dismissed with dismissal signal in the database
* to indicate that the card has been dismissed.
*
* @param context Context
* @param cardName The card name of the ContextualCard which is dismissed by user.
* @return The number of rows updated
*/
int markCardAsDismissed(Context context, String cardName);
/** Log package when user clicks contextual notification channel card. */ /** Log package when user clicks contextual notification channel card. */
void logNotificationPackage(Slice slice); void logNotificationPackage(Slice slice);

View File

@@ -18,10 +18,19 @@ package com.android.settings.homepage.contextualcards;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
import static com.android.settings.homepage.contextualcards.CardDatabaseHelper.CARD_TABLE;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.text.format.DateUtils;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice; import androidx.slice.Slice;
import androidx.slice.SliceMetadata; import androidx.slice.SliceMetadata;
import androidx.slice.core.SliceAction; import androidx.slice.core.SliceAction;
@@ -30,16 +39,46 @@ import com.android.settings.SettingsActivity;
import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppInfoBase;
import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice; import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceRegistry;
import com.android.settingslib.utils.ThreadUtils;
import java.util.Set; import java.util.Set;
public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider { public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider {
private static final String TAG = "ContextualCardFeatureProvider";
private final Context mContext; private final Context mContext;
public ContextualCardFeatureProviderImpl(Context context) { public ContextualCardFeatureProviderImpl(Context context) {
mContext = context; mContext = context;
} }
@Override
public Cursor getContextualCards() {
final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getReadableDatabase();
//TODO(b/149542061): Make the dismissal duration configurable.
final long threshold = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS;
final String selection = CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " < ? OR "
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NULL";
final String[] selectionArgs = {String.valueOf(threshold)};
final Cursor cursor = db.query(CARD_TABLE, null /* columns */, selection,
selectionArgs /* selectionArgs */, null /* groupBy */, null /* having */,
CardDatabaseHelper.CardColumns.SCORE + " DESC" /* orderBy */);
ThreadUtils.postOnBackgroundThread(() -> resetDismissedTime(threshold));
return cursor;
}
@Override
public int markCardAsDismissed(Context context, String cardName) {
final SQLiteDatabase db = CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
final ContentValues values = new ContentValues();
values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, System.currentTimeMillis());
final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
final String[] selectionArgs = {cardName};
final int rowsUpdated = db.update(CARD_TABLE, values, selection, selectionArgs);
context.getContentResolver().notifyChange(CardContentProvider.DELETE_CARD_URI, null);
return rowsUpdated;
}
@Override @Override
public void logNotificationPackage(Slice slice) { public void logNotificationPackage(Slice slice) {
if (slice == null || !slice.getUri().equals( if (slice == null || !slice.getUri().equals(
@@ -62,4 +101,20 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
prefs.edit().putStringSet(ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES, prefs.edit().putStringSet(ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES,
newInteractedPackages).apply(); newInteractedPackages).apply();
} }
@VisibleForTesting
int resetDismissedTime(long threshold) {
final SQLiteDatabase database =
CardDatabaseHelper.getInstance(mContext).getWritableDatabase();
final ContentValues values = new ContentValues();
values.putNull(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP);
final String selection = CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " < ? AND "
+ CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NOT NULL";
final String[] selectionArgs = {String.valueOf(threshold)};
final int rowsUpdated = database.update(CARD_TABLE, values, selection, selectionArgs);
if (Build.IS_DEBUGGABLE) {
Log.d(TAG, "Reset " + rowsUpdated + " records of dismissed time.");
}
return rowsUpdated;
}
} }

View File

@@ -178,7 +178,9 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
@VisibleForTesting @VisibleForTesting
Cursor getContextualCardsFromProvider() { Cursor getContextualCardsFromProvider() {
return CardDatabaseHelper.getInstance(mContext).getContextualCards(); final ContextualCardFeatureProvider cardFeatureProvider =
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
return cardFeatureProvider.getContextualCards();
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -25,9 +25,9 @@ import android.text.TextUtils;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard; import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController; import com.android.settings.homepage.contextualcards.ContextualCardController;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog; import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener; import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils; import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
@@ -68,8 +68,9 @@ public class SliceContextualCardController implements ContextualCardController {
@Override @Override
public void onDismissed(ContextualCard card) { public void onDismissed(ContextualCard card) {
ThreadUtils.postOnBackgroundThread(() -> { ThreadUtils.postOnBackgroundThread(() -> {
final CardDatabaseHelper dbHelper = CardDatabaseHelper.getInstance(mContext); final ContextualCardFeatureProvider cardFeatureProvider =
dbHelper.markContextualCardAsDismissed(mContext, card.getName()); FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
cardFeatureProvider.markCardAsDismissed(mContext, card.getName());
}); });
showFeedbackDialog(card); showFeedbackDialog(card);

View File

@@ -18,13 +18,10 @@ package com.android.settings.homepage.contextualcards;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import com.android.settings.intelligence.ContextualCardProto;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -32,9 +29,6 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class CardDatabaseHelperTest { public class CardDatabaseHelperTest {
@@ -46,7 +40,7 @@ public class CardDatabaseHelperTest {
public void setUp() { public void setUp() {
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext); mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext);
mDatabase = mCardDatabaseHelper.getWritableDatabase(); mDatabase = mCardDatabaseHelper.getReadableDatabase();
} }
@After @After
@@ -69,44 +63,10 @@ public class CardDatabaseHelperTest {
CardDatabaseHelper.CardColumns.CATEGORY, CardDatabaseHelper.CardColumns.CATEGORY,
CardDatabaseHelper.CardColumns.PACKAGE_NAME, CardDatabaseHelper.CardColumns.PACKAGE_NAME,
CardDatabaseHelper.CardColumns.APP_VERSION, CardDatabaseHelper.CardColumns.APP_VERSION,
CardDatabaseHelper.CardColumns.CARD_DISMISSED, CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP,
}; };
assertThat(columnNames).isEqualTo(expectedNames); assertThat(columnNames).isEqualTo(expectedNames);
cursor.close(); cursor.close();
} }
@Test
public void getContextualCards_shouldSortByScore() {
insertFakeCard(mDatabase, "card1", 1, "uri1");
insertFakeCard(mDatabase, "card2", 0, "uri2");
insertFakeCard(mDatabase, "card3", 10, "uri3");
// Should sort as 3,1,2
try (final Cursor cursor = CardDatabaseHelper.getInstance(mContext).getContextualCards()) {
assertThat(cursor.getCount()).isEqualTo(3);
final List<ContextualCard> cards = new ArrayList<>();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
cards.add(new ContextualCard(cursor));
}
assertThat(cards.get(0).getName()).isEqualTo("card3");
assertThat(cards.get(1).getName()).isEqualTo("card1");
assertThat(cards.get(2).getName()).isEqualTo("card2");
}
}
private static void insertFakeCard(SQLiteDatabase db, String name, double score, String uri) {
final ContentValues value = new ContentValues();
value.put(CardDatabaseHelper.CardColumns.NAME, name);
value.put(CardDatabaseHelper.CardColumns.SCORE, score);
value.put(CardDatabaseHelper.CardColumns.SLICE_URI, uri);
value.put(CardDatabaseHelper.CardColumns.TYPE, ContextualCard.CardType.SLICE);
value.put(CardDatabaseHelper.CardColumns.CATEGORY,
ContextualCardProto.ContextualCard.Category.DEFAULT.getNumber());
value.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME,
RuntimeEnvironment.application.getPackageName());
value.put(CardDatabaseHelper.CardColumns.APP_VERSION, 1);
db.insert(CardDatabaseHelper.CARD_TABLE, null, value);
}
} }

View File

@@ -28,10 +28,14 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import android.annotation.Nullable;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.ArraySet; import android.util.ArraySet;
@@ -46,6 +50,7 @@ import androidx.slice.widget.SliceLiveData;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.AppInfoBase;
import com.android.settings.intelligence.ContextualCardProto;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@@ -54,6 +59,8 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
import java.util.Set; import java.util.Set;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -62,6 +69,8 @@ public class ContextualCardFeatureProviderImplTest {
private Context mContext; private Context mContext;
private ContextualCardFeatureProviderImpl mImpl; private ContextualCardFeatureProviderImpl mImpl;
private SharedPreferences mSharedPreferences; private SharedPreferences mSharedPreferences;
private CardDatabaseHelper mCardDatabaseHelper;
private SQLiteDatabase mDatabase;
@Before @Before
public void setUp() { public void setUp() {
@@ -70,13 +79,55 @@ public class ContextualCardFeatureProviderImplTest {
mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE); mSharedPreferences = mContext.getSharedPreferences(PREFS, MODE_PRIVATE);
// Set-up specs for SliceMetadata. // Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mCardDatabaseHelper = CardDatabaseHelper.getInstance(mContext);
mDatabase = mCardDatabaseHelper.getWritableDatabase();
} }
@After @After
public void tearDown() { public void tearDown() {
CardDatabaseHelper.getInstance(mContext).close();
CardDatabaseHelper.sCardDatabaseHelper = null;
removeInteractedPackageFromSharedPreference(); removeInteractedPackageFromSharedPreference();
} }
@Test
public void getContextualCards_shouldSortByScore() {
insertFakeCard(mDatabase, "card1", 1, "uri1", 1000L);
insertFakeCard(mDatabase, "card2", 0, "uri2", 1000L);
insertFakeCard(mDatabase, "card3", 10, "uri3", 1000L);
// Should sort as 3,1,2
try (Cursor cursor = mImpl.getContextualCards()) {
assertThat(cursor.getCount()).isEqualTo(3);
final List<ContextualCard> cards = new ArrayList<>();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
cards.add(new ContextualCard(cursor));
}
assertThat(cards.get(0).getName()).isEqualTo("card3");
assertThat(cards.get(1).getName()).isEqualTo("card1");
assertThat(cards.get(2).getName()).isEqualTo("card2");
}
}
@Test
public void resetDismissedTime_durationExpired_shouldResetToNull() {
insertFakeCard(mDatabase, "card1", 1, "uri1", 100L);
final long threshold = 1000L;
final int rowsUpdated = mImpl.resetDismissedTime(threshold);
assertThat(rowsUpdated).isEqualTo(1);
}
@Test
public void resetDismissedTime_durationNotExpired_shouldNotUpdate() {
insertFakeCard(mDatabase, "card1", 1, "uri1", 1111L);
final long threshold = 1000L;
final int rowsUpdated = mImpl.resetDismissedTime(threshold);
assertThat(rowsUpdated).isEqualTo(0);
}
@Test @Test
public void logNotificationPackage_isContextualNotificationChannel_shouldLogPackage() { public void logNotificationPackage_isContextualNotificationChannel_shouldLogPackage() {
final String packageName = "com.android.test.app"; final String packageName = "com.android.test.app";
@@ -101,6 +152,28 @@ public class ContextualCardFeatureProviderImplTest {
assertThat(interactedPackages.contains(packageName)).isFalse(); assertThat(interactedPackages.contains(packageName)).isFalse();
} }
private static void insertFakeCard(
SQLiteDatabase db, String name, double score, String uri, @Nullable Long time) {
final ContentValues value = new ContentValues();
value.put(CardDatabaseHelper.CardColumns.NAME, name);
value.put(CardDatabaseHelper.CardColumns.SCORE, score);
value.put(CardDatabaseHelper.CardColumns.SLICE_URI, uri);
value.put(CardDatabaseHelper.CardColumns.TYPE, ContextualCard.CardType.SLICE);
value.put(CardDatabaseHelper.CardColumns.CATEGORY,
ContextualCardProto.ContextualCard.Category.DEFAULT.getNumber());
value.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME,
RuntimeEnvironment.application.getPackageName());
value.put(CardDatabaseHelper.CardColumns.APP_VERSION, 1);
if (time == null) {
value.putNull(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP);
} else {
value.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, time);
}
db.insert(CardDatabaseHelper.CARD_TABLE, null, value);
}
private Slice buildSlice(Uri sliceUri, String packageName) { private Slice buildSlice(Uri sliceUri, String packageName) {
final Bundle args = new Bundle(); final Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName); args.putString(AppInfoBase.ARG_PACKAGE_NAME, packageName);

View File

@@ -38,10 +38,13 @@ import com.android.settings.R;
import com.android.settings.homepage.contextualcards.CardContentProvider; import com.android.settings.homepage.contextualcards.CardContentProvider;
import com.android.settings.homepage.contextualcards.CardDatabaseHelper; import com.android.settings.homepage.contextualcards.CardDatabaseHelper;
import com.android.settings.homepage.contextualcards.ContextualCard; import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProviderImpl;
import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog; import com.android.settings.homepage.contextualcards.ContextualCardFeedbackDialog;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -78,10 +81,18 @@ public class SliceContextualCardControllerTest {
mResolver = mContext.getContentResolver(); mResolver = mContext.getContentResolver();
mController = spy(new SliceContextualCardController(mContext)); mController = spy(new SliceContextualCardController(mContext));
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
final ContextualCardFeatureProvider cardFeatureProvider =
new ContextualCardFeatureProviderImpl(mContext);
mFeatureFactory.mContextualCardFeatureProvider = cardFeatureProvider;
}
@After
public void tearDown() {
CardDatabaseHelper.getInstance(mContext).close();
} }
@Test @Test
public void onDismissed_cardShouldBeMarkedAsDismissed() { public void onDismissed_cardShouldBeMarkedAsDismissedWithTimestamp() {
final Uri providerUri = CardContentProvider.REFRESH_CARD_URI; final Uri providerUri = CardContentProvider.REFRESH_CARD_URI;
mResolver.insert(providerUri, generateOneRow()); mResolver.insert(providerUri, generateOneRow());
doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class)); doNothing().when(mController).showFeedbackDialog(any(ContextualCard.class));
@@ -89,15 +100,15 @@ public class SliceContextualCardControllerTest {
final ContextualCard card = getTestSliceCard(); final ContextualCard card = getTestSliceCard();
mController.onDismissed(card); mController.onDismissed(card);
final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED}; final String[] columns = {CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP};
final String selection = CardDatabaseHelper.CardColumns.NAME + "=?"; final String selection = CardDatabaseHelper.CardColumns.NAME + "=?";
final String[] selectionArgs = {TEST_CARD_NAME}; final String[] selectionArgs = {TEST_CARD_NAME};
final Cursor cr = mResolver.query(providerUri, columns, selection, selectionArgs, null); final Cursor cr = mResolver.query(providerUri, columns, selection, selectionArgs, null);
cr.moveToFirst(); cr.moveToFirst();
final int qryDismissed = cr.getInt(0); final long dismissedTimestamp = cr.getLong(0);
cr.close(); cr.close();
assertThat(qryDismissed).isEqualTo(1); assertThat(dismissedTimestamp).isNotEqualTo(0L);
verify(mFeatureFactory.metricsFeatureProvider).action(any(), verify(mFeatureFactory.metricsFeatureProvider).action(any(),
eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS), any(String.class)); eq(SettingsEnums.ACTION_CONTEXTUAL_CARD_DISMISS), any(String.class));
} }
@@ -172,7 +183,7 @@ public class SliceContextualCardControllerTest {
values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2); values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2);
values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings"); values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings");
values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001); values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001);
values.put(CardDatabaseHelper.CardColumns.CARD_DISMISSED, 0); values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, 0L);
return values; return values;
} }

View File

@@ -66,7 +66,6 @@ public class FakeFeatureFactory extends FeatureFactory {
public final UserFeatureProvider userFeatureProvider; public final UserFeatureProvider userFeatureProvider;
public final AssistGestureFeatureProvider assistGestureFeatureProvider; public final AssistGestureFeatureProvider assistGestureFeatureProvider;
public final AccountFeatureProvider mAccountFeatureProvider; public final AccountFeatureProvider mAccountFeatureProvider;
public final ContextualCardFeatureProvider mContextualCardFeatureProvider;
public final BluetoothFeatureProvider mBluetoothFeatureProvider; public final BluetoothFeatureProvider mBluetoothFeatureProvider;
public final AwareFeatureProvider mAwareFeatureProvider; public final AwareFeatureProvider mAwareFeatureProvider;
public final FaceFeatureProvider mFaceFeatureProvider; public final FaceFeatureProvider mFaceFeatureProvider;
@@ -74,6 +73,7 @@ public class FakeFeatureFactory extends FeatureFactory {
public PanelFeatureProvider panelFeatureProvider; public PanelFeatureProvider panelFeatureProvider;
public SlicesFeatureProvider slicesFeatureProvider; public SlicesFeatureProvider slicesFeatureProvider;
public SearchFeatureProvider searchFeatureProvider; public SearchFeatureProvider searchFeatureProvider;
public ContextualCardFeatureProvider mContextualCardFeatureProvider;
/** /**
* Call this in {@code @Before} method of the test class to use fake factory. * Call this in {@code @Before} method of the test class to use fake factory.