OSDN Git Service

Add a filter to take out unnecessary homepage data.
authorEmily Chuang <emilychuang@google.com>
Mon, 1 Oct 2018 11:21:23 +0000 (19:21 +0800)
committerEmily Chuang <emilychuang@google.com>
Tue, 9 Oct 2018 08:20:34 +0000 (16:20 +0800)
After getting the full list from CardContentProvider, at render side
(Settings side) we should perform final filtering to filter out those
data that are not applicable at that moment. Apart from that, we should
also perform slice URI check in this stage to take out invalid slices.

Bug: 116063073
Test: robotests
Change-Id: Idfa94ec6176a9d9cb3379543e0a23858941af742

src/com/android/settings/homepage/CardContentLoader.java
tests/robotests/src/com/android/settings/homepage/CardContentLoaderTest.java
tests/unit/src/com/android/settings/homepage/CardContentLoaderTest.java [new file with mode: 0644]

index 9980503..4fb868a 100644 (file)
 
 package com.android.settings.homepage;
 
+import static android.app.slice.Slice.HINT_ERROR;
+
+import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
+import android.net.Uri;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
+import androidx.slice.Slice;
 
 import com.android.settings.homepage.deviceinfo.DataUsageSlice;
 import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
@@ -30,6 +38,7 @@ import com.android.settingslib.utils.AsyncLoaderCompat;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> {
     private static final String TAG = "CardContentLoader";
@@ -58,18 +67,18 @@ public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> {
         try (Cursor cursor = getContextualCardsFromProvider()) {
             if (cursor.getCount() == 0) {
                 result.addAll(createStaticCards());
-                return result;
-            }
-            for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                final ContextualCard card = new ContextualCard(cursor);
-                if (card.isCustomCard()) {
-                    //TODO(b/114688391): Load and generate custom card,then add into list
-                } else {
-                    result.add(card);
+            } else {
+                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+                    final ContextualCard card = new ContextualCard(cursor);
+                    if (card.isCustomCard()) {
+                        //TODO(b/114688391): Load and generate custom card,then add into list
+                    } else {
+                        result.add(card);
+                    }
                 }
             }
         }
-        return result;
+        return filter(result);
     }
 
     @VisibleForTesting
@@ -94,15 +103,15 @@ public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> {
                     .build());
             //TODO(b/115971399): Will change following values of SliceUri and Name
             // after landing these slice cards.
-            add(new ContextualCard.Builder()
-                    .setSliceUri("content://com.android.settings.slices/intent/battery_card")
-                    .setName(packageName + "/" + "battery_card")
-                    .setPackageName(packageName)
-                    .setRankingScore(rankingScore)
-                    .setAppVersion(appVersionCode)
-                    .setCardType(ContextualCard.CardType.SLICE)
-                    .setIsHalfWidth(true)
-                    .build());
+//            add(new ContextualCard.Builder()
+//                    .setSliceUri("content://com.android.settings.slices/battery_card")
+//                    .setName(packageName + "/" + "battery_card")
+//                    .setPackageName(packageName)
+//                    .setRankingScore(rankingScore)
+//                    .setAppVersion(appVersionCode)
+//                    .setCardType(ContextualCard.CardType.SLICE)
+//                    .setIsHalfWidth(true)
+//                    .build());
             add(new ContextualCard.Builder()
                     .setSliceUri(DeviceInfoSlice.DEVICE_INFO_CARD_URI.toString())
                     .setName(packageName + "/" + DeviceInfoSlice.PATH_DEVICE_INFO_CARD)
@@ -116,6 +125,41 @@ public class CardContentLoader extends AsyncLoaderCompat<List<ContextualCard>> {
         return result;
     }
 
+    @VisibleForTesting
+    List<ContextualCard> filter(List<ContextualCard> candidates) {
+        return candidates.stream().filter(card -> isCardEligibleToDisplay(card)).collect(
+                Collectors.toList());
+    }
+
+    @VisibleForTesting
+    boolean isCardEligibleToDisplay(ContextualCard card) {
+        if (card.isCustomCard()) {
+            return true;
+        }
+
+        final Uri uri = card.getSliceUri();
+
+        if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            return false;
+        }
+
+        //check if the uri has a provider associated with.
+        final ContentProviderClient provider =
+                mContext.getContentResolver().acquireContentProviderClient(uri);
+        if (provider == null) {
+            return false;
+        }
+        //release contentProviderClient to prevent from memory leak.
+        provider.release();
+
+        final Slice slice = Slice.bindSlice(mContext, uri, SUPPORTED_SPECS);
+        if (slice == null || slice.hasHint(HINT_ERROR)) {
+            return false;
+        }
+
+        return true;
+    }
+
     private long getAppVersionCode() {
         try {
             return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(),
index 20ad067..865989d 100644 (file)
@@ -18,128 +18,92 @@ package com.android.settings.homepage;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
 import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
+import android.net.Uri;
 
+import com.android.settings.homepage.deviceinfo.DataUsageSlice;
+import com.android.settings.homepage.deviceinfo.DeviceInfoSlice;
+import com.android.settings.slices.SettingsSliceProvider;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowContentResolver;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class CardContentLoaderTest {
-    private static final String[] QUERY_PROJECTION = {
-            CardDatabaseHelper.CardColumns.NAME,
-            CardDatabaseHelper.CardColumns.TYPE,
-            CardDatabaseHelper.CardColumns.SCORE,
-            CardDatabaseHelper.CardColumns.SLICE_URI,
-            CardDatabaseHelper.CardColumns.CATEGORY,
-            CardDatabaseHelper.CardColumns.LOCALIZED_TO_LOCALE,
-            CardDatabaseHelper.CardColumns.PACKAGE_NAME,
-            CardDatabaseHelper.CardColumns.APP_VERSION,
-            CardDatabaseHelper.CardColumns.TITLE_RES_NAME,
-            CardDatabaseHelper.CardColumns.TITLE_TEXT,
-            CardDatabaseHelper.CardColumns.SUMMARY_RES_NAME,
-            CardDatabaseHelper.CardColumns.SUMMARY_TEXT,
-            CardDatabaseHelper.CardColumns.ICON_RES_NAME,
-            CardDatabaseHelper.CardColumns.ICON_RES_ID,
-            CardDatabaseHelper.CardColumns.CARD_ACTION,
-            CardDatabaseHelper.CardColumns.EXPIRE_TIME_MS,
-            CardDatabaseHelper.CardColumns.SUPPORT_HALF_WIDTH
-    };
 
     private Context mContext;
     private CardContentLoader mCardContentLoader;
+    private SettingsSliceProvider mProvider;
 
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        mCardContentLoader = spy(new CardContentLoader(mContext));
+        mCardContentLoader = new CardContentLoader(mContext);
+        mProvider = new SettingsSliceProvider();
+        ShadowContentResolver.registerProviderInternal(SettingsSliceProvider.SLICE_AUTHORITY,
+                mProvider);
     }
 
     @Test
-    public void loadInBackground_hasDataInDb_shouldReturnData() {
-        final Cursor cursor = generateTwoRowContextualCards();
-        doReturn(cursor).when(mCardContentLoader).getContextualCardsFromProvider();
-
-        final List<ContextualCard> contextualCards = mCardContentLoader.loadInBackground();
+    public void createStaticCards_shouldReturnTwoCards() {
+        final List<ContextualCard> defaultData = mCardContentLoader.createStaticCards();
 
-        assertThat(contextualCards.size()).isEqualTo(cursor.getCount());
+        assertThat(defaultData).hasSize(2);
     }
 
     @Test
-    public void loadInBackground_hasNoData_shouldReturnThreeDefaultData() {
-        final Cursor cursor = generateEmptyContextualCards();
-        doReturn(cursor).when(mCardContentLoader).getContextualCardsFromProvider();
+    public void createStaticCards_shouldContainDataUsageAndDeviceInfo() {
+        final Uri dataUsage = DataUsageSlice.DATA_USAGE_CARD_URI;
+        final Uri deviceInfo = DeviceInfoSlice.DEVICE_INFO_CARD_URI;
+        final List<Uri> expectedUris = Arrays.asList(dataUsage, deviceInfo);
 
-        final List<ContextualCard> contextualCards = mCardContentLoader.loadInBackground();
+        final List<Uri> actualCardUris = mCardContentLoader.createStaticCards().stream().map(
+                ContextualCard::getSliceUri).collect(Collectors.toList());
 
-        assertThat(contextualCards.size()).isEqualTo(mCardContentLoader.createStaticCards().size());
+        assertThat(actualCardUris).containsExactlyElementsIn(expectedUris);
     }
 
-    private MatrixCursor generateEmptyContextualCards() {
-        final MatrixCursor result = new MatrixCursor(QUERY_PROJECTION);
-        return result;
+    @Test
+    public void isCardEligibleToDisplay_customCard_returnTrue() {
+        final ContextualCard customCard = new ContextualCard.Builder()
+                .setName("custom_card")
+                .setCardType(ContextualCard.CardType.DEFAULT)
+                .setTitleText("custom_title")
+                .setSummaryText("custom_summary")
+                .build();
+
+        assertThat(mCardContentLoader.isCardEligibleToDisplay(customCard)).isTrue();
     }
 
-    private MatrixCursor generateTwoRowContextualCards() {
-        final MatrixCursor result = generateEmptyContextualCards();
-        result.addRow(generateFirstFakeData());
-        result.addRow(generateSecondFakeData());
-        return result;
+    @Test
+    public void isCardEligibleToDisplay_invalidScheme_returnFalse() {
+        final String sliceUri = "contet://com.android.settings.slices/action/flashlight";
+
+        assertThat(
+                mCardContentLoader.isCardEligibleToDisplay(getContextualCard(sliceUri))).isFalse();
     }
 
-    private Object[] generateFirstFakeData() {
-        final Object[] ref = new Object[]{
-                "auto_rotate", /* NAME */
-                ContextualCard.CardType.SLICE, /* TYPE */
-                0.5, /* SCORE */
-                "content://com.android.settings.slices/action/auto_rotate", /* SLICE_URI */
-                2, /* CATEGORY */
-                "", /* LOCALIZED_TO_LOCALE */
-                "com.android.settings", /* PACKAGE_NAME */
-                1l, /* APP_VERSION */
-                "", /* TITLE_RES_NAME */
-                "", /* TITLE_TEXT */
-                "", /* SUMMARY_RES_NAME */
-                "", /* SUMMARY_TEXT */
-                "", /* ICON_RES_NAME */
-                0, /* ICON_RES_ID */
-                0, /* CARD_ACTION */
-                -1, /* EXPIRE_TIME_MS */
-                0 /* SUPPORT_HALF_WIDTH */
-        };
-        return ref;
+    @Test
+    public void isCardEligibleToDisplay_noProvider_returnFalse() {
+        final String sliceUri = "content://com.android.settings.test.slices/action/flashlight";
+
+        assertThat(
+                mCardContentLoader.isCardEligibleToDisplay(getContextualCard(sliceUri))).isFalse();
     }
 
-    private Object[] generateSecondFakeData() {
-        final Object[] ref = new Object[]{
-                "toggle_airplane", /* NAME */
-                ContextualCard.CardType.SLICE, /* TYPE */
-                0.5, /* SCORE */
-                "content://com.android.settings.slices/action/toggle_airplane", /* SLICE_URI */
-                2, /* CATEGORY */
-                "", /* LOCALIZED_TO_LOCALE */
-                "com.android.settings", /* PACKAGE_NAME */
-                1l, /* APP_VERSION */
-                "", /* TITLE_RES_NAME */
-                "", /* TITLE_TEXT */
-                "", /* SUMMARY_RES_NAME */
-                "", /* SUMMARY_TEXT */
-                "", /* ICON_RES_NAME */
-                0, /* ICON_RES_ID */
-                0, /* CARD_ACTION */
-                -1, /* EXPIRE_TIME_MS */
-                0 /* SUPPORT_HALF_WIDTH */
-        };
-        return ref;
+    private ContextualCard getContextualCard(String sliceUri) {
+        return new ContextualCard.Builder()
+                .setName("test_card")
+                .setCardType(ContextualCard.CardType.SLICE)
+                .setSliceUri(sliceUri)
+                .build();
     }
 }
diff --git a/tests/unit/src/com/android/settings/homepage/CardContentLoaderTest.java b/tests/unit/src/com/android/settings/homepage/CardContentLoaderTest.java
new file mode 100644 (file)
index 0000000..1f762bb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CardContentLoaderTest {
+
+    private Context mContext;
+    private CardContentLoader mCardContentLoader;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mCardContentLoader = new CardContentLoader(mContext);
+    }
+
+    @Test
+    public void filter_twoInvalidCards_shouldReturnOneCard() {
+        final String sliceUri1 = "content://com.android.settings.slices/action/flashlight"; //valid
+        final String sliceUri2 = "content://com.android.settings.test.slices/action/flashlight";
+        final String sliceUri3 = "cotent://com.android.settings.slices/action/flashlight";
+
+        final List<ContextualCard> cards = new ArrayList<>();
+        cards.add(getContextualCard(sliceUri1));
+        cards.add(getContextualCard(sliceUri2));
+        cards.add(getContextualCard(sliceUri3));
+
+        final List<ContextualCard> result = mCardContentLoader.filter(cards);
+
+        assertThat(result).hasSize(1);
+    }
+
+    private ContextualCard getContextualCard(String sliceUri) {
+        return new ContextualCard.Builder()
+                .setName("test_card")
+                .setCardType(ContextualCard.CardType.SLICE)
+                .setSliceUri(sliceUri)
+                .build();
+    }
+}