Loading src/com/android/settings/homepage/contextualcards/CardContentProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java +23 −52 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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"; Loading Loading @@ -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); Loading Loading @@ -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; } } src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java +15 −0 Original line number Diff line number Diff line Loading @@ -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); Loading src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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( Loading @@ -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; } } src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
src/com/android/settings/homepage/contextualcards/CardContentProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -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"; Loading
src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java +23 −52 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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"; Loading Loading @@ -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); Loading Loading @@ -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; } }
src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProvider.java +15 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
src/com/android/settings/homepage/contextualcards/ContextualCardFeatureProviderImpl.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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( Loading @@ -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; } }
src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +3 −1 Original line number Diff line number Diff line Loading @@ -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 Loading