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()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(CardContentProvider.CARD_AUTHORITY)
.appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED)
.appendPath(CardDatabaseHelper.CardColumns.DISMISSED_TIMESTAMP)
.build();
private static final String TAG = "CardContentProvider";

View File

@@ -16,9 +16,7 @@
package com.android.settings.homepage.contextualcards;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
@@ -31,7 +29,7 @@ import androidx.annotation.VisibleForTesting;
public class CardDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "CardDatabaseHelper";
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";
@@ -72,31 +70,32 @@ public class CardDatabaseHelper extends SQLiteOpenHelper {
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 =
"CREATE TABLE " + CARD_TABLE +
"(" +
CardColumns.NAME +
" TEXT NOT NULL PRIMARY KEY, " +
CardColumns.TYPE +
" INTEGER NOT NULL, " +
CardColumns.SCORE +
" DOUBLE NOT NULL, " +
CardColumns.SLICE_URI +
" TEXT, " +
CardColumns.CATEGORY +
" INTEGER DEFAULT 0, " +
CardColumns.PACKAGE_NAME +
" TEXT NOT NULL, " +
CardColumns.APP_VERSION +
" INTEGER NOT NULL, " +
CardColumns.CARD_DISMISSED +
" INTEGER DEFAULT 0 " +
");";
"CREATE TABLE "
+ CARD_TABLE
+ "("
+ CardColumns.NAME
+ " TEXT NOT NULL PRIMARY KEY, "
+ CardColumns.TYPE
+ " INTEGER NOT NULL, "
+ CardColumns.SCORE
+ " DOUBLE NOT NULL, "
+ CardColumns.SLICE_URI
+ " TEXT, "
+ CardColumns.CATEGORY
+ " INTEGER DEFAULT 0, "
+ CardColumns.PACKAGE_NAME
+ " TEXT NOT NULL, "
+ CardColumns.APP_VERSION
+ " INTEGER NOT NULL, "
+ CardColumns.DISMISSED_TIMESTAMP
+ " INTEGER"
+ ");";
public CardDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -125,32 +124,4 @@ public class CardDatabaseHelper extends SQLiteOpenHelper {
}
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;
import android.content.Context;
import android.database.Cursor;
import androidx.slice.Slice;
/** Feature provider for the contextual card feature. */
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. */
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 com.android.settings.homepage.contextualcards.CardDatabaseHelper.CARD_TABLE;
import android.content.ContentValues;
import android.content.Context;
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.Log;
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
import androidx.slice.core.SliceAction;
@@ -30,16 +39,46 @@ 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 com.android.settingslib.utils.ThreadUtils;
import java.util.Set;
public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureProvider {
private static final String TAG = "ContextualCardFeatureProvider";
private final Context mContext;
public ContextualCardFeatureProviderImpl(Context 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
public void logNotificationPackage(Slice slice) {
if (slice == null || !slice.getUri().equals(
@@ -62,4 +101,20 @@ public class ContextualCardFeatureProviderImpl implements ContextualCardFeatureP
prefs.edit().putStringSet(ContextualNotificationChannelSlice.PREF_KEY_INTERACTED_PACKAGES,
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
Cursor getContextualCardsFromProvider() {
return CardDatabaseHelper.getInstance(mContext).getContextualCards();
final ContextualCardFeatureProvider cardFeatureProvider =
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
return cardFeatureProvider.getContextualCards();
}
@VisibleForTesting

View File

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