diff --git a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java index 808814352b9..e7ede14d663 100644 --- a/src/com/android/settings/homepage/contextualcards/CardContentProvider.java +++ b/src/com/android/settings/homepage/contextualcards/CardContentProvider.java @@ -17,6 +17,7 @@ package com.android.settings.homepage.contextualcards; import android.content.ContentProvider; +import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; @@ -36,10 +37,15 @@ import com.android.settingslib.utils.ThreadUtils; */ public class CardContentProvider extends ContentProvider { - private static final String TAG = "CardContentProvider"; - public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider"; + public static final Uri URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(CardContentProvider.CARD_AUTHORITY) + .appendPath(CardDatabaseHelper.CARD_TABLE) + .build(); + + private static final String TAG = "CardContentProvider"; /** URI matcher for ContentProvider queries. */ private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); /** URI matcher type for cards table */ diff --git a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java index b5cdf87cf53..164afdd7d68 100644 --- a/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java +++ b/src/com/android/settings/homepage/contextualcards/CardDatabaseHelper.java @@ -209,16 +209,19 @@ public class CardDatabaseHelper extends SQLiteOpenHelper { * Mark a specific ContextualCard with dismissal flag in the database to indicate that the * card has been dismissed. * - * @param cardName the card name of the ContextualCard which is dismissed by user. - * @return updated row number + * @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(String cardName) { - final SQLiteDatabase database = this.getWritableDatabase(); + 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.URI, null); return rowsUpdated; } } diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java index adfaf201cd6..c723cfd0245 100644 --- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java +++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java @@ -24,8 +24,11 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; @@ -60,6 +63,19 @@ public class ContextualCardLoader extends AsyncLoaderCompat mContext = context.getApplicationContext(); } + @Override + protected void onStartLoading() { + super.onStartLoading(); + mContext.getContentResolver().registerContentObserver(CardContentProvider.URI, + false /*notifyForDescendants*/, mObserver); + } + + @Override + protected void onStopLoading() { + super.onStopLoading(); + mContext.getContentResolver().unregisterContentObserver(mObserver); + } + @Override protected void onDiscardResult(List result) { @@ -184,4 +200,14 @@ public class ContextualCardLoader extends AsyncLoaderCompat } return -1L; } + + private final ContentObserver mObserver = new ContentObserver( + new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + if (isStarted()) { + forceLoad(); + } + } + }; } diff --git a/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java b/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java index 723b3443734..931bfb70d41 100644 --- a/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java +++ b/src/com/android/settings/homepage/contextualcards/ControllerRendererPool.java @@ -114,7 +114,7 @@ public class ControllerRendererPool { if (ConditionContextualCardController.class == clz) { return new ConditionContextualCardController(context); } else if (SliceContextualCardController.class == clz) { - return new SliceContextualCardController(); + return new SliceContextualCardController(context); } else if (LegacySuggestionContextualCardController.class == clz) { return new LegacySuggestionContextualCardController(context); } diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java index 84ceabdab1f..6fcc636d140 100644 --- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java +++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardController.java @@ -77,13 +77,13 @@ public class ConditionContextualCardController implements ContextualCardControll @Override public void onActionClick(ContextualCard contextualCard) { - + final ConditionalContextualCard card = (ConditionalContextualCard) contextualCard; + mConditionManager.onActionClick(card.getConditionId()); } @Override public void onDismissed(ContextualCard contextualCard) { - final ConditionalContextualCard card = (ConditionalContextualCard) contextualCard; - mConditionManager.onActionClick(card.getConditionId()); + } @Override @@ -93,8 +93,8 @@ public class ConditionContextualCardController implements ContextualCardControll final boolean isOddNumber = conditionCards.size() % 2 == 1; if (isOddNumber) { final int lastIndex = conditionCards.size() - 1; - final ConditionalContextualCard card = (ConditionalContextualCard) conditionCards.get( - lastIndex); + final ConditionalContextualCard card = (ConditionalContextualCard) conditionCards + .get(lastIndex); conditionCards.set(lastIndex, card.mutate().setIsHalfWidth(false).build()); } diff --git a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java index 3405dd2cb25..34f442c500e 100644 --- a/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java +++ b/src/com/android/settings/homepage/contextualcards/conditional/ConditionContextualCardRenderer.java @@ -113,7 +113,7 @@ public class ConditionContextualCardRenderer implements ContextualCardRenderer { viewContext, MetricsProto.MetricsEvent.ACTION_SETTINGS_CONDITION_BUTTON, card.getMetricsConstant()); mControllerRendererPool.getController(mContext, card.getCardType()) - .onDismissed(card); + .onActionClick(card); }); } else { button.setVisibility(View.GONE); diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java index 6ab8f404352..f1fbc9cb83a 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java +++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardController.java @@ -16,9 +16,14 @@ package com.android.settings.homepage.contextualcards.slices; +import android.content.Context; + +import com.android.settings.homepage.contextualcards.CardContentProvider; +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.ContextualCardUpdateListener; +import com.android.settingslib.utils.ThreadUtils; /** * Card controller for {@link ContextualCard} built as slices. @@ -27,8 +32,13 @@ public class SliceContextualCardController implements ContextualCardController { private static final String TAG = "SliceCardController"; + private Context mContext; private ContextualCardUpdateListener mCardUpdateListener; + public SliceContextualCardController(Context context) { + mContext = context; + } + @Override public int getCardType() { return ContextualCard.CardType.SLICE; @@ -46,11 +56,14 @@ public class SliceContextualCardController implements ContextualCardController { @Override public void onDismissed(ContextualCard card) { - //TODO(b/113783548): Mark this card as dismissed in db and reload loader. + ThreadUtils.postOnBackgroundThread(() -> { + final CardDatabaseHelper dbHelper = CardDatabaseHelper.getInstance(mContext); + dbHelper.markContextualCardAsDismissed(mContext, card.getName()); + }); } @Override public void setCardUpdateListener(ContextualCardUpdateListener listener) { - mCardUpdateListener = listener; + mCardUpdateListener = listener; } } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java new file mode 100644 index 00000000000..362e1f541ac --- /dev/null +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardControllerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.homepage.contextualcards.slices; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +import com.android.settings.homepage.contextualcards.CardContentProvider; +import com.android.settings.homepage.contextualcards.CardDatabaseHelper; +import com.android.settings.homepage.contextualcards.ContextualCard; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowContentResolver; + +@RunWith(SettingsRobolectricTestRunner.class) +public class SliceContextualCardControllerTest { + + private static final String TEST_SLICE_URI = "content://test/test"; + private static final String TEST_CARD_NAME = "test_card_name"; + + private Context mContext; + private CardContentProvider mProvider; + private ContentResolver mResolver; + private SliceContextualCardController mController; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mProvider = Robolectric.setupContentProvider(CardContentProvider.class); + ShadowContentResolver.registerProviderInternal(CardContentProvider.CARD_AUTHORITY, + mProvider); + mResolver = mContext.getContentResolver(); + mController = new SliceContextualCardController(mContext); + } + + @Test + public void onDismissed_cardShouldBeMarkedAsDismissed() { + final Uri providerUri = CardContentProvider.URI; + final ContextualCard card = new ContextualCard.Builder() + .setName(TEST_CARD_NAME) + .setCardType(ContextualCard.CardType.SLICE) + .setSliceUri(Uri.parse(TEST_SLICE_URI)) + .build(); + mResolver.insert(providerUri, generateOneRow()); + + mController.onDismissed(card); + + final String[] columns = {CardDatabaseHelper.CardColumns.CARD_DISMISSED}; + final String selection = CardDatabaseHelper.CardColumns.NAME + "=?"; + final String[] selectionArgs = {TEST_CARD_NAME}; + final Cursor cr = mResolver.query(providerUri, columns, selection, selectionArgs, null); + cr.moveToFirst(); + final int qryDismissed = cr.getInt(0); + cr.close(); + + assertThat(qryDismissed).isEqualTo(1); + } + + private ContentValues generateOneRow() { + final ContentValues values = new ContentValues(); + values.put(CardDatabaseHelper.CardColumns.NAME, TEST_CARD_NAME); + values.put(CardDatabaseHelper.CardColumns.TYPE, 1); + values.put(CardDatabaseHelper.CardColumns.SCORE, 0.9); + values.put(CardDatabaseHelper.CardColumns.SLICE_URI, TEST_SLICE_URI); + 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.CARD_DISMISSED, 0); + + return values; + } +}