Keep contextual card dismissal info upon deletion
Add a config for contextual dismissal. This config is used to
determine whether the existing dismissal timestamp should be
kept before an insertion.
The new dismissal behaviour design replaces the boolean
value(isDismissed) with a long value(dismissedTimestamp) to give
this behaviour more flexibility. Utilizing the timestamp allows
developers to define new criteria of card's display (e.g cards being
dismissed can be shown again after a period of time).
While newly inserted data from SettingsIntelligence or other clients
may not have dismissal timestamp recorded. Turning on the config gives
the capability to persist dismissal timestamp, and more customized
behaviour can be created.
Bug: 143055685
Test: robotests
Change-Id: I9d095955e9c51f2aa3332d49ee230d3ded9ae744
Merged-In: I9d095955e9c51f2aa3332d49ee230d3ded9ae744
(cherry picked from commit 377125bf96
)
This commit is contained in:
@@ -201,6 +201,9 @@
|
|||||||
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
|
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
|
||||||
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
|
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool>
|
||||||
|
|
||||||
|
<!-- Whether dismissal timestamp should be kept before deletion -->
|
||||||
|
<bool name="config_keep_contextual_card_dismissal_timestamp">false</bool>
|
||||||
|
|
||||||
<!-- Settings intelligence package name -->
|
<!-- Settings intelligence package name -->
|
||||||
<string name="config_settingsintelligence_package_name" translatable="false">
|
<string name="config_settingsintelligence_package_name" translatable="false">
|
||||||
com.android.settings.intelligence
|
com.android.settings.intelligence
|
||||||
|
@@ -26,12 +26,16 @@ import android.database.sqlite.SQLiteQueryBuilder;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider stores and manages user interaction feedback for homepage contextual cards.
|
* Provider stores and manages user interaction feedback for homepage contextual cards.
|
||||||
*/
|
*/
|
||||||
@@ -81,6 +85,9 @@ public class CardContentProvider extends ContentProvider {
|
|||||||
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
|
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
|
||||||
int numInserted = 0;
|
int numInserted = 0;
|
||||||
final SQLiteDatabase database = mDBHelper.getWritableDatabase();
|
final SQLiteDatabase database = mDBHelper.getWritableDatabase();
|
||||||
|
final boolean keepDismissalTimestampBeforeDeletion = getContext().getResources()
|
||||||
|
.getBoolean(R.bool.config_keep_contextual_card_dismissal_timestamp);
|
||||||
|
final Map<String, Long> dismissedTimeMap = new ArrayMap<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
maybeEnableStrictMode();
|
maybeEnableStrictMode();
|
||||||
@@ -88,9 +95,42 @@ public class CardContentProvider extends ContentProvider {
|
|||||||
final String table = getTableFromMatch(uri);
|
final String table = getTableFromMatch(uri);
|
||||||
database.beginTransaction();
|
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 */);
|
database.delete(table, null /* whereClause */, null /* whereArgs */);
|
||||||
|
|
||||||
for (ContentValues value : values) {
|
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);
|
long ret = database.insert(table, null /* nullColumnHack */, value);
|
||||||
if (ret != -1L) {
|
if (ret != -1L) {
|
||||||
numInserted++;
|
numInserted++;
|
||||||
|
@@ -72,6 +72,8 @@
|
|||||||
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
|
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
|
||||||
<bool name="config_enable_extra_screen_zoom_preview">false</bool>
|
<bool name="config_enable_extra_screen_zoom_preview">false</bool>
|
||||||
|
|
||||||
|
<!-- Whether dismissal timestamp should be kept before deletion -->
|
||||||
|
<bool name="config_keep_contextual_card_dismissal_timestamp">true</bool>
|
||||||
|
|
||||||
<!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
|
<!-- List of a11y components on the device allowed to be enabled by Settings Slices -->
|
||||||
<string-array name="config_settings_slices_accessibility_components" translatable="false">
|
<string-array name="config_settings_slices_accessibility_components" translatable="false">
|
||||||
|
@@ -38,6 +38,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.robolectric.Robolectric;
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
import org.robolectric.util.ReflectionHelpers;
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@@ -85,6 +86,25 @@ public class CardContentProviderTest {
|
|||||||
assertThat(rowsAfterInsert - rowsBeforeInsert).isEqualTo(2);
|
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
|
@Test
|
||||||
public void cardData_query() {
|
public void cardData_query() {
|
||||||
mResolver.insert(mUri, generateOneRow());
|
mResolver.insert(mUri, generateOneRow());
|
||||||
@@ -198,10 +218,40 @@ public class CardContentProviderTest {
|
|||||||
return twoRows;
|
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() {
|
private int getRowCount() {
|
||||||
final Cursor cr = mResolver.query(mUri, null, null, null);
|
final Cursor cr = mResolver.query(mUri, null, null, null);
|
||||||
final int count = cr.getCount();
|
final int count = cr.getCount();
|
||||||
cr.close();
|
cr.close();
|
||||||
return count;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user