| /* |
| * Copyright 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 androidx.slice.widget; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.TypedArray; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.net.Uri; |
| import android.util.TypedValue; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.TextView; |
| |
| import androidx.core.graphics.drawable.IconCompat; |
| import androidx.recyclerview.widget.RecyclerView; |
| import androidx.slice.Slice; |
| import androidx.slice.SliceMetadata; |
| import androidx.slice.SliceProvider; |
| import androidx.slice.builders.ListBuilder; |
| import androidx.slice.builders.SliceAction; |
| import androidx.slice.render.SliceRenderActivity; |
| import androidx.slice.view.test.R; |
| import androidx.test.annotation.UiThreadTest; |
| import androidx.test.core.app.ApplicationProvider; |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.filters.SdkSuppress; |
| import androidx.test.platform.app.InstrumentationRegistry; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Tests for {@link SliceView}. |
| */ |
| @RunWith(AndroidJUnit4.class) |
| @MediumTest |
| @SdkSuppress(minSdkVersion = 19) |
| public class SliceViewTest { |
| |
| private final Context mContext = ApplicationProvider.getApplicationContext(); |
| private SliceView mSliceView; |
| |
| @Before |
| @UiThreadTest |
| public void setup() { |
| mContext.setTheme(R.style.AppTheme); |
| mSliceView = new SliceView(mContext); |
| SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); |
| } |
| |
| @Test |
| public void testSetSlice() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| mSliceView.setSlice(s); |
| |
| assertEquals(s, mSliceView.getSlice()); |
| } |
| |
| @Test |
| public void testSetNullSlice() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| assertEquals(s, mSliceView.getSlice()); |
| |
| mSliceView.setSlice(null); |
| assertNull(mSliceView.getSlice()); |
| } |
| |
| @Test |
| public void testSetScrollable() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| assertTrue(mSliceView.isScrollable()); |
| |
| mSliceView.setScrollable(false); |
| assertFalse(mSliceView.isScrollable()); |
| |
| mSliceView.setScrollable(true); |
| assertTrue(mSliceView.isScrollable()); |
| } |
| |
| @Test |
| public void testGetActionsNull() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| mSliceView.setSlice(s); |
| |
| List<androidx.slice.core.SliceAction> actualActions = mSliceView.getSliceActions(); |
| assertNull(actualActions); |
| } |
| |
| @Test |
| public void testGetActions() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction a1 = new SliceAction(getIntent(""), icon, "action1"); |
| SliceAction a2 = new SliceAction(getIntent(""), icon, "action2"); |
| SliceAction a3 = new SliceAction(getIntent(""), icon, "action3"); |
| SliceAction a4 = new SliceAction(getIntent(""), icon, "action4"); |
| |
| ArrayList<SliceAction> expectedActions = new ArrayList<>(); |
| expectedActions.add(a1); |
| expectedActions.add(a2); |
| expectedActions.add(a3); |
| expectedActions.add(a4); |
| |
| for (SliceAction action : expectedActions) { |
| lb.addAction(action); |
| } |
| |
| mSliceView.setSlice(lb.build()); |
| List<androidx.slice.core.SliceAction> actualActions = mSliceView.getSliceActions(); |
| for (int i = 0; i < expectedActions.size(); i++) { |
| assertEquivalent(expectedActions.get(i), actualActions.get(i)); |
| } |
| } |
| |
| @Test |
| public void testSortSliceActions() { |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction a1 = new SliceAction(getIntent(""), icon, "action1").setPriority(0); |
| SliceAction a2 = new SliceAction(getIntent(""), icon, "action2").setPriority(1); |
| SliceAction a3 = new SliceAction(getIntent(""), icon, "action3").setPriority(2); |
| SliceAction a4 = new SliceAction(getIntent(""), icon, "action4").setPriority(10); |
| SliceAction a5 = new SliceAction(getIntent(""), icon, "action5").setPriority(-1); |
| |
| ArrayList<SliceAction> actions = new ArrayList<>(); |
| actions.add(a2); |
| actions.add(a3); |
| actions.add(a5); |
| actions.add(a1); |
| actions.add(a4); |
| |
| ArrayList<SliceAction> expectedActions = new ArrayList<>(); |
| expectedActions.add(a1); |
| expectedActions.add(a2); |
| expectedActions.add(a3); |
| expectedActions.add(a4); |
| expectedActions.add(a5); |
| |
| Collections.sort(actions, SliceView.SLICE_ACTION_PRIORITY_COMPARATOR); |
| |
| for (int i = 0; i < expectedActions.size(); i++) { |
| assertEquivalent(expectedActions.get(i), actions.get(i)); |
| } |
| } |
| |
| @Test |
| public void testSetValidActions() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction a1 = new SliceAction(getIntent(""), icon, "action1"); |
| SliceAction a2 = new SliceAction(getIntent(""), icon, "action2"); |
| SliceAction a3 = new SliceAction(getIntent(""), icon, "action3"); |
| SliceAction a4 = new SliceAction(getIntent(""), icon, "action4"); |
| lb.addAction(a1); |
| lb.addAction(a2); |
| lb.addAction(a3); |
| lb.addAction(a4); |
| |
| mSliceView.setSlice(lb.build()); |
| List<androidx.slice.core.SliceAction> originalActions = mSliceView.getSliceActions(); |
| |
| List<androidx.slice.core.SliceAction> actionsToSet = new ArrayList<>(); |
| actionsToSet.add(originalActions.get(1)); |
| actionsToSet.add(originalActions.get(3)); |
| |
| mSliceView.setSliceActions(actionsToSet); |
| |
| List<androidx.slice.core.SliceAction> actualActions = mSliceView.getSliceActions(); |
| |
| for (int i = 0; i < actionsToSet.size(); i++) { |
| assertEquivalent(actionsToSet.get(i), actualActions.get(i)); |
| } |
| } |
| |
| @Test |
| public void testSetNullActionsOnHeader() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| PendingIntent pi = getIntent(""); |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction action1 = SliceAction.create(pi, icon, ListBuilder.SMALL_IMAGE, "action1"); |
| SliceAction action2 = SliceAction.createDeeplink(pi, icon, |
| ListBuilder.SMALL_IMAGE, "action2"); |
| SliceAction action3 = SliceAction.create(pi, icon, ListBuilder.SMALL_IMAGE, "action3"); |
| |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.setHeader(new ListBuilder.HeaderBuilder() |
| .setTitle("Text") |
| .setPrimaryAction(getAction("Action"))) |
| .addAction(action1) |
| .addAction(action2) |
| .addAction(action3); |
| |
| ArrayList<SliceAction> expectedActions = new ArrayList<>(); |
| expectedActions.add(action1); |
| expectedActions.add(action2); |
| expectedActions.add(action3); |
| |
| mSliceView.setSlice(lb.build()); |
| |
| List<androidx.slice.core.SliceAction> actualActions = mSliceView.getSliceActions(); |
| for (int i = 0; i < expectedActions.size(); i++) { |
| assertEquivalent(expectedActions.get(i), actualActions.get(i)); |
| } |
| |
| mSliceView.setSliceActions(null); |
| assertNull(mSliceView.getSliceActions()); |
| } |
| |
| @Test |
| public void testSetNullActionsOnRow() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| PendingIntent pi = getIntent(""); |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction action1 = SliceAction.create(pi, icon, ListBuilder.SMALL_IMAGE, "action1"); |
| SliceAction action2 = SliceAction.createDeeplink(pi, icon, |
| ListBuilder.SMALL_IMAGE, "action2"); |
| SliceAction action3 = SliceAction.create(pi, icon, ListBuilder.SMALL_IMAGE, "action3"); |
| |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Text") |
| .setPrimaryAction(getAction("Action")) |
| .addEndItem(action1) |
| .addEndItem(action2) |
| .addEndItem(action3)); |
| |
| ArrayList<SliceAction> expectedActions = new ArrayList<>(); |
| expectedActions.add(action1); |
| expectedActions.add(action2); |
| expectedActions.add(action3); |
| |
| mSliceView.setSlice(lb.build()); |
| |
| List<androidx.slice.core.SliceAction> actualActions = mSliceView.getSliceActions(); |
| for (int i = 0; i < expectedActions.size(); i++) { |
| assertEquivalent(expectedActions.get(i), actualActions.get(i)); |
| } |
| |
| mSliceView.setSliceActions(null); |
| assertNull(mSliceView.getSliceActions()); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testSetInvalidActions() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| SliceAction a1 = new SliceAction(getIntent(""), icon, "action1"); |
| SliceAction a2 = new SliceAction(getIntent(""), icon, "action2"); |
| SliceAction a3 = new SliceAction(getIntent(""), icon, "action3"); |
| SliceAction a4 = new SliceAction(getIntent(""), icon, "action4"); |
| lb.addAction(a1); |
| lb.addAction(a2); |
| lb.addAction(a3); |
| lb.addAction(a4); |
| |
| mSliceView.setSlice(lb.build()); |
| |
| List<androidx.slice.core.SliceAction> actionsToSet = new ArrayList<>(); |
| actionsToSet.add(new SliceAction(getIntent(""), icon, "action1")); |
| actionsToSet.add(new SliceAction(getIntent(""), icon, "action2")); |
| |
| mSliceView.setSliceActions(actionsToSet); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testSetActionsNullSlice() { |
| mSliceView.setSlice(null); |
| |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| |
| List<androidx.slice.core.SliceAction> actionsToSet = new ArrayList<>(); |
| actionsToSet.add(new SliceAction(getIntent(""), icon, "action1")); |
| actionsToSet.add(new SliceAction(getIntent(""), icon, "action2")); |
| |
| mSliceView.setSliceActions(actionsToSet); |
| } |
| |
| @Test |
| public void testHeightMin() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| SliceViewPolicy p = new SliceViewPolicy(); |
| mSliceView.setMode(SliceView.MODE_SMALL); |
| mSliceView.setSliceViewPolicy(p); |
| |
| mSliceView.setSlice(new ListBuilder(mContext, uri, ListBuilder.INFINITY) |
| .addInputRange(new ListBuilder.InputRangeBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(SliceAction.createToggle(getIntent(""), "Switch", true)) |
| .setMin(0) |
| .setMax(5) |
| .setInputAction(getIntent(""))) |
| .build()); |
| |
| // Test a height between min and max heights, full width because that doesn't matter. |
| int width = mContext.getResources().getDisplayMetrics().widthPixels; |
| int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, |
| mContext.getResources().getDisplayMetrics()); |
| mSliceView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); |
| mSliceView.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), |
| View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); |
| |
| assertEquals(height, mSliceView.getMeasuredHeight()); |
| assertEquals(height, p.getMaxSmallHeight()); |
| } |
| |
| @Test |
| public void testHeightBetweenMinAndMax() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| SliceViewPolicy p = new SliceViewPolicy(); |
| mSliceView.setMode(SliceView.MODE_SMALL); |
| mSliceView.setSliceViewPolicy(p); |
| |
| mSliceView.setSlice(new ListBuilder(mContext, uri, ListBuilder.INFINITY) |
| .addInputRange(new ListBuilder.InputRangeBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(SliceAction.createToggle(getIntent(""), "Switch", true)) |
| .setMin(0) |
| .setMax(5) |
| .setInputAction(getIntent(""))) |
| .build()); |
| |
| // Test a height between min and max heights, full width because that doesn't matter. |
| int width = mContext.getResources().getDisplayMetrics().widthPixels; |
| int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 49, |
| mContext.getResources().getDisplayMetrics()); |
| mSliceView.setLayoutParams(new ViewGroup.LayoutParams(width, height)); |
| mSliceView.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), |
| View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); |
| |
| assertEquals(height, mSliceView.getMeasuredHeight()); |
| assertEquals(height, p.getMaxSmallHeight()); |
| } |
| |
| @Test |
| public void testDefaultHideTitleItems() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitleItem(getAction("Action")) |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| |
| RowContent row = (RowContent) mSliceView.mListContent.getRowItems().get(0); |
| assertFalse(row.hasTitleItems()); |
| assertNull(row.getStartItem()); |
| } |
| |
| @Test |
| public void testShowTitleItems() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitleItem(getAction("Action")) |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| mSliceView.setShowTitleItems(true); |
| |
| RowContent row = (RowContent) mSliceView.mListContent.getRowItems().get(0); |
| assertTrue(row.hasTitleItems()); |
| assertNotNull(row.getStartItem()); |
| |
| mSliceView.setShowTitleItems(false); |
| |
| assertFalse(row.hasTitleItems()); |
| assertNull(row.getStartItem()); |
| } |
| |
| @Test |
| public void testHideHeaderDividerWhenOnlyOneRow() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| mSliceView.setShowHeaderDivider(true); |
| |
| assertFalse(mSliceView.mListContent.getHeader().hasBottomDivider()); |
| } |
| |
| @Test |
| public void testShowHeaderDivider() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| mSliceView.setShowHeaderDivider(true); |
| |
| assertTrue(mSliceView.mListContent.getHeader().hasBottomDivider()); |
| } |
| |
| @Test |
| public void testDefaultHideActionDividers() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| |
| RowContent row = (RowContent) mSliceView.mListContent.getRowItems().get(0); |
| assertFalse(row.hasActionDivider()); |
| } |
| |
| @Test |
| public void testShowActionDividers() { |
| Uri uri = Uri.parse("content://pkg/slice"); |
| ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY); |
| lb.addRow(new ListBuilder.RowBuilder() |
| .setTitle("Title") |
| .setSubtitle("Subtitle") |
| .setPrimaryAction(getAction("Action"))); |
| Slice s = lb.build(); |
| |
| mSliceView.setSlice(s); |
| mSliceView.setShowActionDividers(true); |
| |
| RowContent row = (RowContent) mSliceView.mListContent.getRowItems().get(0); |
| assertTrue(row.hasActionDivider()); |
| } |
| |
| @Test |
| public void testSetRowStyleFactory() { |
| // Create a slice with an unchecked and a checked SliceAction. |
| Uri uri = Uri.parse("content://pkg/slice"); |
| Slice s = new ListBuilder(mContext, uri, ListBuilder.INFINITY) |
| .addRow(new ListBuilder.RowBuilder() |
| .setTitle("Header")) |
| .addRow(new ListBuilder.RowBuilder() |
| .setTitle("Unchecked") |
| .setSubtitle("Unchecked Subtitle") |
| .setTitleItem( |
| SliceAction.createToggle(getIntent("Check"), "checkbox", false))) |
| .addRow(new ListBuilder.RowBuilder() |
| .setTitle("Checked") |
| .setSubtitle("Checked Subtitle") |
| .setTitleItem( |
| SliceAction.createToggle(getIntent("Uncheck"), "checkbox", true))) |
| .build(); |
| |
| // Use an alternative style for checked items. |
| mSliceView.setRowStyleFactory(sliceItem -> { |
| androidx.slice.core.SliceAction action = SliceMetadata.from( |
| mContext, sliceItem.getSlice()).getPrimaryAction(); |
| if (action != null && action.isToggle() && action.isChecked()) { |
| return R.style.CheckedSliceRowStyle; |
| } |
| // Use the default style otherwise. |
| return 0; |
| }); |
| |
| mSliceView.setSlice(s); |
| |
| // Lay out the SliceView to initialize the row views. |
| InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { |
| @Override |
| public void run() { |
| mSliceView.measure(View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.EXACTLY), |
| View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.UNSPECIFIED)); |
| mSliceView.layout(0, 0, 500, 1000); |
| } |
| }); |
| |
| // Expected colors for checked items. |
| int checkedTitleColor = mContext.getResources().getColor( |
| R.color.checkedItemTitleColor, mContext.getTheme()); |
| int checkedSubtitleColor = mContext.getResources().getColor( |
| R.color.checkedItemSubtitleColor, mContext.getTheme()); |
| |
| // Expected colors for unchecked items (the default theme colors). |
| int themeTitleColor = getThemeColor(android.R.attr.textColorPrimary); |
| int themeSubtitleColor = getThemeColor(android.R.attr.textColorSecondary); |
| |
| RecyclerView recyclerView = (RecyclerView) find(mSliceView, RecyclerView.class); |
| assertNotNull(recyclerView); |
| |
| // The checked item has the checked row style. |
| TextView checkedTitleView = recyclerView.findViewHolderForAdapterPosition(2) |
| .itemView.findViewById(android.R.id.title); |
| assertEquals("Checked", checkedTitleView.getText()); |
| assertEquals(checkedTitleColor, checkedTitleView.getCurrentTextColor()); |
| |
| TextView checkedSubtitleView = recyclerView.findViewHolderForAdapterPosition(2) |
| .itemView.findViewById(android.R.id.summary); |
| assertEquals("Checked Subtitle", checkedSubtitleView.getText()); |
| assertEquals(checkedSubtitleColor, checkedSubtitleView.getCurrentTextColor()); |
| |
| // The unchecked item has the default style. |
| TextView uncheckedTitleView = recyclerView.findViewHolderForAdapterPosition(1) |
| .itemView.findViewById(android.R.id.title); |
| assertEquals("Unchecked", uncheckedTitleView.getText()); |
| assertEquals(themeTitleColor, uncheckedTitleView.getCurrentTextColor()); |
| |
| TextView uncheckedSubtitleView = recyclerView.findViewHolderForAdapterPosition(1) |
| .itemView.findViewById(android.R.id.summary); |
| assertEquals("Unchecked Subtitle", uncheckedSubtitleView.getText()); |
| assertEquals(themeSubtitleColor, uncheckedSubtitleView.getCurrentTextColor()); |
| } |
| |
| @Test |
| @UiThreadTest |
| public void testHeaderRowHidden_onlyTwoRowsFit_showsSeeMoreRow() { |
| // Create a slice with 3 content rows. |
| Uri uri = Uri.parse("content://pkg/slice"); |
| Slice s = new ListBuilder(mContext, uri, ListBuilder.INFINITY) |
| .addRow(new ListBuilder.RowBuilder() |
| .setTitle("Header") |
| .setPrimaryAction(getAction("Action"))) |
| .addRow(new ListBuilder.RowBuilder().setTitle("0")) |
| .addRow(new ListBuilder.RowBuilder().setTitle("1")) |
| .addRow(new ListBuilder.RowBuilder().setTitle("2")) |
| .setSeeMoreRow(new ListBuilder.RowBuilder().setTitle("See more")) |
| .build(); |
| |
| // This slice view has hideHeaderRow set to true. |
| View inflatedView = LayoutInflater.from(mContext).inflate( |
| R.layout.hide_header_row_slice_view, /* root= */ null); |
| SliceView sliceView = inflatedView.findViewById(R.id.sliceTestSliceView); |
| sliceView.setScrollable(false); |
| sliceView.setSlice(s); |
| |
| // Lay out the slice such that exactly 2 rows fit. |
| int rowHeightPixels = mContext.getResources().getDimensionPixelSize( |
| R.dimen.abc_slice_action_row_height); |
| sliceView.measure( |
| View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.EXACTLY), |
| View.MeasureSpec.makeMeasureSpec(2 * rowHeightPixels, View.MeasureSpec.EXACTLY)); |
| sliceView.layout(0, 0, 2 * rowHeightPixels, 1000); |
| |
| RecyclerView recyclerView = (RecyclerView) find(sliceView, RecyclerView.class); |
| assertEquals(2, recyclerView.getAdapter().getItemCount()); |
| TextView view0 = recyclerView.findViewHolderForAdapterPosition(0) |
| .itemView.findViewById(android.R.id.title); |
| TextView view1 = recyclerView.findViewHolderForAdapterPosition(1) |
| .itemView.findViewById(android.R.id.title); |
| assertEquals("0", view0.getText()); |
| assertEquals("See more", view1.getText()); |
| } |
| |
| @Test |
| @UiThreadTest |
| public void testHeaderRowHidden_onlyOneRowFits_showsHeaderRow() { |
| // Create a slice with 2 content rows. |
| Uri uri = Uri.parse("content://pkg/slice"); |
| Slice s = new ListBuilder(mContext, uri, ListBuilder.INFINITY) |
| .addRow(new ListBuilder.RowBuilder() |
| .setTitle("Header") |
| .setPrimaryAction(getAction("Action"))) |
| .addRow(new ListBuilder.RowBuilder().setTitle("0")) |
| .addRow(new ListBuilder.RowBuilder().setTitle("1")) |
| .setSeeMoreRow(new ListBuilder.RowBuilder().setTitle("See more")) |
| .build(); |
| |
| // This slice view has hideHeaderRow set to true. |
| View inflatedView = LayoutInflater.from(mContext).inflate( |
| R.layout.hide_header_row_slice_view, /* root= */ null); |
| SliceView sliceView = inflatedView.findViewById(R.id.sliceTestSliceView); |
| sliceView.setScrollable(false); |
| sliceView.setSlice(s); |
| |
| // Lay out the slice such that exactly 1 row fits. |
| int rowHeightPixels = mContext.getResources().getDimensionPixelSize( |
| R.dimen.abc_slice_action_row_height); |
| sliceView.measure( |
| View.MeasureSpec.makeMeasureSpec(500, View.MeasureSpec.EXACTLY), |
| View.MeasureSpec.makeMeasureSpec(rowHeightPixels, View.MeasureSpec.EXACTLY)); |
| sliceView.layout(0, 0, rowHeightPixels, 1000); |
| |
| // It doesn't make sense to display only "See more". It also doesn't make sense |
| // to only show a single content row without "See more. Hence, we show the |
| // header row. |
| RecyclerView recyclerView = (RecyclerView) find(sliceView, RecyclerView.class); |
| assertEquals(1, recyclerView.getAdapter().getItemCount()); |
| TextView view0 = recyclerView.findViewHolderForAdapterPosition(0) |
| .itemView.findViewById(android.R.id.title); |
| assertEquals("Header", view0.getText()); |
| } |
| |
| private int getThemeColor(int colorRes) { |
| TypedValue typedValue = new TypedValue(); |
| mContext.getTheme().resolveAttribute(colorRes, typedValue, true); |
| TypedArray arr = mContext.obtainStyledAttributes(typedValue.data, new int[]{ |
| colorRes}); |
| int themeColor = arr.getColor(0, -1); |
| assertNotSame(-1, themeColor); |
| return themeColor; |
| } |
| |
| private View find(View v, Class<?> desiredClass) { |
| if (desiredClass.isInstance(v)) { |
| return v; |
| } |
| if (v instanceof ViewGroup) { |
| ViewGroup vg = (ViewGroup) v; |
| for (int i = 0; i < vg.getChildCount(); i++) { |
| v = find(vg.getChildAt(i), desiredClass); |
| if (v != null) { |
| return v; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private SliceAction getAction(String actionName) { |
| Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); |
| new Canvas(b).drawColor(0xffff0000); |
| IconCompat icon = IconCompat.createWithBitmap(b); |
| return new SliceAction(getIntent(""), icon, actionName); |
| } |
| |
| private PendingIntent getIntent(String action) { |
| Intent intent = new Intent(action); |
| intent.setClassName(mContext.getPackageName(), SliceRenderActivity.class.getName()); |
| return PendingIntent.getActivity(mContext, 0, intent, 0); |
| } |
| |
| private void assertEquivalent(androidx.slice.core.SliceAction desired, |
| androidx.slice.core.SliceAction actual) { |
| assertEquals(desired.getTitle(), actual.getTitle()); |
| assertEquals(desired.getContentDescription(), actual.getContentDescription()); |
| assertEquals(desired.isToggle(), actual.isToggle()); |
| assertEquals(desired.isDefaultToggle(), actual.isDefaultToggle()); |
| assertEquals(desired.isChecked(), actual.isChecked()); |
| assertEquals(desired.getPriority(), actual.getPriority()); |
| assertEquals(desired.getIcon() == null, actual.getIcon() == null); |
| assertEquals(desired.getImageMode(), actual.getImageMode()); |
| } |
| } |