diff --git a/res/values/config.xml b/res/values/config.xml index 2373b253aaa..64d9ab7081f 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -201,6 +201,9 @@ true + + false + com.android.settings.intelligence diff --git a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java index 9627eb03521..75ec651fc00 100644 --- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java +++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java @@ -26,12 +26,16 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.Build; import android.os.StrictMode; +import android.util.ArrayMap; import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settings.R; import com.android.settingslib.utils.ThreadUtils; +import java.util.Map; + /** * Provider stores and manages user interaction feedback for homepage contextual cards. */ @@ -40,10 +44,10 @@ public class CardContentProvider extends ContentProvider { public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider"; public static final Uri REFRESH_CARD_URI = new Uri.Builder() - .scheme(ContentResolver.SCHEME_CONTENT) - .authority(CardContentProvider.CARD_AUTHORITY) - .appendPath(CardDatabaseHelper.CARD_TABLE) - .build(); + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(CardContentProvider.CARD_AUTHORITY) + .appendPath(CardDatabaseHelper.CARD_TABLE) + .build(); public static final Uri DELETE_CARD_URI = new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) @@ -81,6 +85,9 @@ public class CardContentProvider extends ContentProvider { final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); int numInserted = 0; final SQLiteDatabase database = mDBHelper.getWritableDatabase(); + final boolean keepDismissalTimestampBeforeDeletion = getContext().getResources() + .getBoolean(R.bool.config_keep_contextual_card_dismissal_timestamp); + final Map dismissedTimeMap = new ArrayMap<>(); try { maybeEnableStrictMode(); @@ -88,9 +95,42 @@ public class CardContentProvider extends ContentProvider { final String table = getTableFromMatch(uri); database.beginTransaction(); - // Here deletion first is avoiding redundant insertion. According to cl/215350754 + if (keepDismissalTimestampBeforeDeletion) { + // Query the existing db and get dismissal info. + final String[] columns = new String[]{CardDatabaseHelper.CardColumns.NAME, + CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP}; + final String selection = + CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP + " IS NOT NULL"; + try (Cursor cursor = database.query(table, columns, selection, + null/* selectionArgs */, null /* groupBy */, + null /* having */, null /* orderBy */)) { + // Save them to a Map + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + final String cardName = cursor.getString(cursor.getColumnIndex( + CardDatabaseHelper.CardColumns.NAME)); + final long timestamp = cursor.getLong(cursor.getColumnIndex( + CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP)); + dismissedTimeMap.put(cardName, timestamp); + } + } + } + + // Here delete data first to avoid redundant insertion. According to cl/215350754 database.delete(table, null /* whereClause */, null /* whereArgs */); + for (ContentValues value : values) { + if (keepDismissalTimestampBeforeDeletion) { + // Replace dismissedTimestamp in each value if there is an old one. + final String cardName = + value.get(CardDatabaseHelper.CardColumns.NAME).toString(); + if (dismissedTimeMap.containsKey(cardName)) { + // Replace the value of dismissedTimestamp + value.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, + dismissedTimeMap.get(cardName)); + Log.d(TAG, "Replace dismissed time: " + cardName); + } + } + long ret = database.insert(table, null /* nullColumnHack */, value); if (ret != -1L) { numInserted++; diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml index dc638a58001..c7173bc61d8 100644 --- a/tests/robotests/res/values-mcc999/config.xml +++ b/tests/robotests/res/values-mcc999/config.xml @@ -72,6 +72,8 @@ false + + true diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java index d13c97c47da..32af5d8597d 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/CardContentProviderTest.java @@ -38,6 +38,7 @@ import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @@ -85,6 +86,25 @@ public class CardContentProviderTest { assertThat(rowsAfterInsert - rowsBeforeInsert).isEqualTo(2); } + @Test + @Config(qualifiers = "mcc999") + public void bulkInsert_keepDismissalTimestamp_shouldHaveTimestamp() { + mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp()); + + mResolver.bulkInsert(mUri, generateTwoRows()); + + assertThat(queryDismissedTimestamp()).isEqualTo(10001L); + } + + @Test + public void bulkInsert_notKeepDismissalTimestamp_shouldNotHaveTimestamp() { + mResolver.bulkInsert(mUri, generateTwoRowsWithDismissTimestamp()); + + mResolver.bulkInsert(mUri, generateTwoRows()); + + assertThat(queryDismissedTimestamp()).isEqualTo(0L); + } + @Test public void cardData_query() { mResolver.insert(mUri, generateOneRow()); @@ -198,10 +218,40 @@ public class CardContentProviderTest { return twoRows; } + private ContentValues[] generateTwoRowsWithDismissTimestamp() { + final ContentValues[] twoRows = new ContentValues[2]; + twoRows[0] = generateOneRow(); + + final ContentValues values = new ContentValues(); + values.put(CardDatabaseHelper.CardColumns.NAME, "toggle_airplane"); + values.put(CardDatabaseHelper.CardColumns.TYPE, 1); + values.put(CardDatabaseHelper.CardColumns.SCORE, 0.95); + values.put(CardDatabaseHelper.CardColumns.SLICE_URI, + "content://com.android.settings.slices/action/toggle_airplane"); + values.put(CardDatabaseHelper.CardColumns.CATEGORY, 2); + values.put(CardDatabaseHelper.CardColumns.PACKAGE_NAME, "com.android.settings"); + values.put(CardDatabaseHelper.CardColumns.APP_VERSION, 10001); + values.put(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP, 10001L); + twoRows[1] = values; + + return twoRows; + } + private int getRowCount() { final Cursor cr = mResolver.query(mUri, null, null, null); final int count = cr.getCount(); cr.close(); return count; } + + private long queryDismissedTimestamp() { + final String[] columns = {CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP}; + final String selection = CardDatabaseHelper.CardColumns.NAME + "=?"; + final String[] selectionArgs = {"toggle_airplane"}; + final Cursor cr = mResolver.query(mUri, columns, selection, selectionArgs, null); + cr.moveToFirst(); + final long dismissedTimestamp = cr.getLong(0); + cr.close(); + return dismissedTimestamp; + } } \ No newline at end of file