Improve UX of Deferred Setup

Change-Id: I3d0735ef1196b04abaef454529664a8daea53967
Bug: 120485678
Test: visual, robotests
This commit is contained in:
Yanting Yang
2019-01-18 23:39:15 +08:00
parent 875178bbfa
commit 2784da75a2
10 changed files with 462 additions and 29 deletions

View File

@@ -19,6 +19,7 @@ message ContextualCard {
POSSIBLE = 2;
IMPORTANT = 3;
EXCLUSIVE = 4;
DEFERRED_SETUP = 5;
}
/** Slice uri of the contextual card */

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 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.
-->
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/ContextualCardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ViewFlipper
android:id="@+id/view_flipper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="left"
android:orientation="vertical"
android:paddingBottom="@dimen/homepage_deferred_setup_card_padding_bottom"
android:paddingEnd="@dimen/homepage_card_padding_end"
android:paddingStart="@dimen/homepage_card_padding_start"
android:paddingTop="@dimen/homepage_deferred_setup_card_padding_top">
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/homepage_card_icon_size"
android:layout_height="@dimen/homepage_card_icon_size"/>
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/homepage_deferred_setup_card_title_margin_top"
android:ellipsize="end"
android:maxLines="2"
android:minLines="1"
android:textAppearance="@style/TextAppearance.DeferredSetupCardTitle"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/homepage_deferred_setup_card_summary_margin_top"
android:ellipsize="end"
android:maxLines="2"
android:minLines="1"
android:textAppearance="@style/TextAppearance.DeferredSetupCardSummary"/>
<Button
android:id="@+id/finish_setup"
style="@style/DeferredSetupCardButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/homepage_deferred_setup_card_button_margin_top"
android:text="@string/suggestion_button_text"/>
</LinearLayout>
<!--dismissal view-->
<include layout="@layout/homepage_dismissal_view"/>
</ViewFlipper>
</com.google.android.material.card.MaterialCardView>

View File

@@ -333,6 +333,15 @@
<dimen name="homepage_half_card_padding_top">12dp</dimen>
<dimen name="homepage_half_card_padding_bottom">16dp</dimen>
<dimen name="homepage_half_card_title_margin_top">12dp</dimen>
<dimen name="homepage_deferred_setup_card_padding_top">16dp</dimen>
<dimen name="homepage_deferred_setup_card_padding_bottom">12dp</dimen>
<dimen name="homepage_deferred_setup_card_title_margin_top">12dp</dimen>
<dimen name="homepage_deferred_setup_card_summary_margin_top">2dp</dimen>
<dimen name="homepage_deferred_setup_card_button_margin_top">8dp</dimen>
<dimen name="homepage_deferred_setup_card_button_padding_top">8dp</dimen>
<dimen name="homepage_deferred_setup_card_button_padding_bottom">8dp</dimen>
<dimen name="homepage_deferred_setup_card_button_padding_start">24dp</dimen>
<dimen name="homepage_deferred_setup_card_button_padding_end">24dp</dimen>
<!-- Homepage dismissal cards size and padding -->
<dimen name="homepage_card_dismissal_margin_top">16dp</dimen>

View File

@@ -469,4 +469,24 @@
<item name="titleSize">@*android:dimen/text_size_subhead_material</item>
</style>
<style name="TextAppearance.DeferredSetupCardTitle">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.DeferredSetupCardSummary"
parent="@*android:style/TextAppearance.DeviceDefault.Body1">
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="DeferredSetupCardButton" parent="android:Widget.DeviceDefault.Button.Colored">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:paddingBottom">@dimen/homepage_deferred_setup_card_button_padding_bottom</item>
<item name="android:paddingEnd">@dimen/homepage_deferred_setup_card_button_padding_end</item>
<item name="android:paddingStart">@dimen/homepage_deferred_setup_card_button_padding_start</item>
<item name="android:paddingTop">@dimen/homepage_deferred_setup_card_button_padding_top</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
</style>
</resources>

View File

@@ -79,6 +79,10 @@ public class ContextualCardLookupTable {
LegacySuggestionContextualCardRenderer.VIEW_TYPE,
LegacySuggestionContextualCardController.class,
LegacySuggestionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.SLICE,
SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP,
SliceContextualCardController.class,
SliceContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.SLICE,
SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
SliceContextualCardController.class,

View File

@@ -17,6 +17,7 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE;
import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE;
import static java.util.stream.Collectors.groupingBy;
@@ -71,17 +72,15 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
@VisibleForTesting
final List<ContextualCard> mContextualCards;
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
private final Lifecycle mLifecycle;
private final List<LifecycleObserver> mLifecycleObservers;
@VisibleForTesting
long mStartTime;
boolean mIsFirstLaunch;
@VisibleForTesting
List<String> mSavedCards;
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
private final Lifecycle mLifecycle;
private final List<LifecycleObserver> mLifecycleObservers;
private ContextualCardUpdateListener mListener;
public ContextualCardManager(Context context, Lifecycle lifecycle, Bundle savedInstanceState) {
@@ -175,7 +174,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
//replace with the new data
mContextualCards.clear();
final List<ContextualCard> sortedCards = sortCards(allCards);
mContextualCards.addAll(assignCardWidth(sortedCards));
mContextualCards.addAll(getCardsWithViewType(sortedCards));
loadCardControllers();
@@ -228,10 +227,19 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
}
@VisibleForTesting
List<ContextualCard> assignCardWidth(List<ContextualCard> cards) {
final List<ContextualCard> result = new ArrayList<>(cards);
List<ContextualCard> getCardsWithViewType(List<ContextualCard> cards) {
if (cards.isEmpty()) {
return cards;
}
final List<ContextualCard> result = getCardsWithDeferredSetupViewType(cards);
return getCardsWithSuggestionViewType(result);
}
private List<ContextualCard> getCardsWithSuggestionViewType(List<ContextualCard> cards) {
// Shows as half cards if 2 suggestion type of cards are next to each other.
// Shows as full card if 1 suggestion type of card lives alone.
final List<ContextualCard> result = new ArrayList<>(cards);
for (int index = 1; index < result.size(); index++) {
final ContextualCard previous = result.get(index - 1);
final ContextualCard current = result.get(index);
@@ -247,6 +255,22 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
return result;
}
private List<ContextualCard> getCardsWithDeferredSetupViewType(List<ContextualCard> cards) {
// Find the deferred setup card and assign it with proper view type.
// Reason: The returned card list will mix deferred setup card and other suggestion cards
// after device running 1 days.
final List<ContextualCard> result = new ArrayList<>(cards);
for (int index = 0; index < result.size(); index++) {
final ContextualCard card = cards.get(index);
if (card.getCategory() == DEFERRED_SETUP_VALUE) {
result.set(index, card.mutate().setViewType(
SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP).build());
return result;
}
}
return result;
}
private List<ContextualCard> getCardsToKeep(List<ContextualCard> cards) {
if (mSavedCards != null) {
//screen rotate

View File

@@ -52,6 +52,7 @@ import java.util.Set;
public class SliceContextualCardRenderer implements ContextualCardRenderer, LifecycleObserver {
public static final int VIEW_TYPE_FULL_WIDTH = R.layout.homepage_slice_tile;
public static final int VIEW_TYPE_HALF_WIDTH = R.layout.homepage_slice_half_tile;
public static final int VIEW_TYPE_DEFERRED_SETUP = R.layout.homepage_slice_deferred_setup_tile;
private static final String TAG = "SliceCardRenderer";
@@ -64,6 +65,7 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
private final LifecycleOwner mLifecycleOwner;
private final ControllerRendererPool mControllerRendererPool;
private final Set<ContextualCard> mCardSet;
private final SliceDeferredSetupCardRendererHelper mDeferredSetupCardHelper;
private final SliceFullCardRendererHelper mFullCardHelper;
private final SliceHalfCardRendererHelper mHalfCardHelper;
@@ -78,11 +80,14 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
mLifecycleOwner.getLifecycle().addObserver(this);
mFullCardHelper = new SliceFullCardRendererHelper(context);
mHalfCardHelper = new SliceHalfCardRendererHelper(context);
mDeferredSetupCardHelper = new SliceDeferredSetupCardRendererHelper(context);
}
@Override
public RecyclerView.ViewHolder createViewHolder(View view, @LayoutRes int viewType) {
switch (viewType) {
case VIEW_TYPE_DEFERRED_SETUP:
return mDeferredSetupCardHelper.createViewHolder(view);
case VIEW_TYPE_HALF_WIDTH:
return mHalfCardHelper.createViewHolder(view);
default:
@@ -119,17 +124,25 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
//TODO(b/120629936): Take this out once blank card issue is fixed.
Log.d(TAG, "Slice callback - uri = " + slice.getUri());
}
if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
mHalfCardHelper.bindView(holder, card, slice);
} else {
mFullCardHelper.bindView(holder, card, slice, mCardSet);
switch (holder.getItemViewType()) {
case VIEW_TYPE_DEFERRED_SETUP:
mDeferredSetupCardHelper.bindView(holder, card, slice);
break;
case VIEW_TYPE_HALF_WIDTH:
mHalfCardHelper.bindView(holder, card, slice);
break;
default:
mFullCardHelper.bindView(holder, card, slice, mCardSet);
}
});
if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
initDismissalActions(holder, card, R.id.content);
} else {
initDismissalActions(holder, card, R.id.slice_view);
switch (holder.getItemViewType()) {
case VIEW_TYPE_DEFERRED_SETUP:
case VIEW_TYPE_HALF_WIDTH:
initDismissalActions(holder, card, R.id.content);
break;
default:
initDismissalActions(holder, card, R.id.slice_view);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2019 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 android.app.PendingIntent;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
import androidx.slice.core.SliceAction;
import androidx.slice.widget.EventInfo;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
/**
* Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
*/
class SliceDeferredSetupCardRendererHelper {
private static final String TAG = "SliceDSCRendererHelper";
private final Context mContext;
SliceDeferredSetupCardRendererHelper(Context context) {
mContext = context;
}
RecyclerView.ViewHolder createViewHolder(View view) {
return new DeferredSetupCardViewHolder(view);
}
void bindView(RecyclerView.ViewHolder holder, ContextualCard card, Slice slice) {
final DeferredSetupCardViewHolder view = (DeferredSetupCardViewHolder) holder;
final SliceMetadata sliceMetadata = SliceMetadata.from(mContext, slice);
final SliceAction primaryAction = sliceMetadata.getPrimaryAction();
view.icon.setImageDrawable(primaryAction.getIcon().loadDrawable(mContext));
view.title.setText(primaryAction.getTitle());
view.summary.setText(sliceMetadata.getSubtitle());
view.button.setOnClickListener(v -> {
try {
primaryAction.getAction().send();
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
}
final ContextualCardFeatureProvider contextualCardFeatureProvider =
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider(mContext);
contextualCardFeatureProvider.logContextualCardClick(card, 0 /* row */,
EventInfo.ACTION_TYPE_CONTENT);
});
}
static class DeferredSetupCardViewHolder extends RecyclerView.ViewHolder {
public final LinearLayout content;
public final ImageView icon;
public final TextView title;
public final TextView summary;
public final Button button;
public DeferredSetupCardViewHolder(View itemView) {
super(itemView);
content = itemView.findViewById(R.id.content);
icon = itemView.findViewById(android.R.id.icon);
title = itemView.findViewById(android.R.id.title);
summary = itemView.findViewById(android.R.id.summary);
button = itemView.findViewById(R.id.finish_setup);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH;
@@ -210,7 +211,7 @@ public class ContextualCardManagerTest {
@Test
public void assignCardWidth_noSuggestionCards_shouldNotHaveHalfCards() {
public void getCardsWithViewType_noSuggestionCards_shouldNotHaveHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -221,7 +222,7 @@ public class ContextualCardManagerTest {
final List<ContextualCard> noSuggestionCards = buildCategoriedCards(getContextualCardList(),
categories);
final List<ContextualCard> result = mManager.assignCardWidth(noSuggestionCards);
final List<ContextualCard> result = mManager.getCardsWithViewType(noSuggestionCards);
assertThat(result).hasSize(5);
for (ContextualCard card : result) {
@@ -230,7 +231,7 @@ public class ContextualCardManagerTest {
}
@Test
public void assignCardWidth_oneSuggestionCards_shouldNotHaveHalfCards() {
public void getCardsWithViewType_oneSuggestionCards_shouldNotHaveHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -241,7 +242,7 @@ public class ContextualCardManagerTest {
final List<ContextualCard> oneSuggestionCards = buildCategoriedCards(
getContextualCardList(), categories);
final List<ContextualCard> result = mManager.assignCardWidth(oneSuggestionCards);
final List<ContextualCard> result = mManager.getCardsWithViewType(oneSuggestionCards);
assertThat(result).hasSize(5);
for (ContextualCard card : result) {
@@ -250,7 +251,7 @@ public class ContextualCardManagerTest {
}
@Test
public void assignCardWidth_twoConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
public void getCardsWithViewType_twoConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -264,7 +265,7 @@ public class ContextualCardManagerTest {
VIEW_TYPE_FULL_WIDTH, VIEW_TYPE_HALF_WIDTH, VIEW_TYPE_HALF_WIDTH,
VIEW_TYPE_FULL_WIDTH);
final List<ContextualCard> result = mManager.assignCardWidth(
final List<ContextualCard> result = mManager.getCardsWithViewType(
twoConsecutiveSuggestionCards);
assertThat(result).hasSize(5);
@@ -274,7 +275,7 @@ public class ContextualCardManagerTest {
}
@Test
public void assignCardWidth_twoNonConsecutiveSuggestionCards_shouldNotHaveHalfCards() {
public void getCardsWithViewType_twoNonConsecutiveSuggestionCards_shouldNotHaveHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -285,7 +286,7 @@ public class ContextualCardManagerTest {
final List<ContextualCard> twoNonConsecutiveSuggestionCards = buildCategoriedCards(
getContextualCardList(), categories);
final List<ContextualCard> result = mManager.assignCardWidth(
final List<ContextualCard> result = mManager.getCardsWithViewType(
twoNonConsecutiveSuggestionCards);
assertThat(result).hasSize(5);
@@ -295,7 +296,7 @@ public class ContextualCardManagerTest {
}
@Test
public void assignCardWidth_threeConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
public void getCardsWithViewType_threeConsecutiveSuggestionCards_shouldHaveTwoHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
@@ -309,7 +310,7 @@ public class ContextualCardManagerTest {
VIEW_TYPE_HALF_WIDTH, VIEW_TYPE_HALF_WIDTH, VIEW_TYPE_FULL_WIDTH,
VIEW_TYPE_FULL_WIDTH);
final List<ContextualCard> result = mManager.assignCardWidth(
final List<ContextualCard> result = mManager.getCardsWithViewType(
threeConsecutiveSuggestionCards);
assertThat(result).hasSize(5);
@@ -319,7 +320,7 @@ public class ContextualCardManagerTest {
}
@Test
public void assignCardWidth_fourConsecutiveSuggestionCards_shouldHaveFourHalfCards() {
public void getCardsWithViewType_fourConsecutiveSuggestionCards_shouldHaveFourHalfCards() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
@@ -333,7 +334,7 @@ public class ContextualCardManagerTest {
VIEW_TYPE_HALF_WIDTH, VIEW_TYPE_HALF_WIDTH, VIEW_TYPE_HALF_WIDTH,
VIEW_TYPE_HALF_WIDTH);
final List<ContextualCard> result = mManager.assignCardWidth(
final List<ContextualCard> result = mManager.getCardsWithViewType(
fourConsecutiveSuggestionCards);
assertThat(result).hasSize(5);
@@ -342,6 +343,55 @@ public class ContextualCardManagerTest {
}
}
@Test
public void getCardsWithViewType_onlyDeferredSetupCard_shouldHaveDeferredSetupCard() {
final List<ContextualCard> oneDeferredSetupCards = getDeferredSetupCardList();
final List<ContextualCard> result = mManager.getCardsWithViewType(oneDeferredSetupCards);
assertThat(result).hasSize(1);
assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
}
@Test
public void getCardsWithViewType_hasDeferredSetupCard_shouldHaveDeferredSetupCard() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
);
final List<ContextualCard> cards = buildCategoriedCards(getContextualCardList(),
categories);
final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
assertThat(result).hasSize(5);
assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
}
@Test
public void getCardsWithViewType_noDeferredSetupCard_shouldNotHaveDeferredSetupCard() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
);
final List<ContextualCard> cards = buildCategoriedCards(
getContextualCardList(), categories);
final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
assertThat(result).hasSize(5);
for (int i = 0; i < result.size(); i++) {
assertThat(result.get(i).getViewType()).isNotEqualTo(
ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE);
}
}
private ContextualCard buildContextualCard(String sliceUri) {
return new ContextualCard.Builder()
.setName(TEST_SLICE_NAME)
@@ -396,4 +446,16 @@ public class ContextualCardManagerTest {
.build());
return cards;
}
private List<ContextualCard> getDeferredSetupCardList() {
final List<ContextualCard> cards = new ArrayList<>();
cards.add(new ContextualCard.Builder()
.setName("deferred_setup")
.setCardType(ContextualCard.CardType.SLICE)
.setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
.setSliceUri(new Uri.Builder().appendPath("test_deferred_setup_path").build())
.setViewType(VIEW_TYPE_FULL_WIDTH)
.build());
return cards;
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2019 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.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
import static com.android.settings.homepage.contextualcards.slices.SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder;
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import androidx.core.graphics.drawable.IconCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.intelligence.ContextualCardProto;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class SliceDeferredSetupCardRendererHelperTest {
private static final Uri TEST_SLICE_URI = Uri.parse("content://test/test");
private static final CharSequence TITLE = "test_title";
private static final CharSequence SUMMARY = "test_summary";
private Activity mActivity;
private SliceDeferredSetupCardRendererHelper mHelper;
@Before
public void setUp() {
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
mActivity = Robolectric.buildActivity(Activity.class).create().get();
mActivity.setTheme(R.style.Theme_Settings_Home);
mHelper = new SliceDeferredSetupCardRendererHelper(mActivity);
}
@Test
public void createViewHolder_shouldAlwaysReturnCustomViewHolder() {
final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
assertThat(viewHolder).isInstanceOf(
DeferredSetupCardViewHolder.class);
}
@Test
public void bindView_shouldSetTitle() {
final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
assertThat(((DeferredSetupCardViewHolder) viewHolder).title.getText()).isEqualTo(TITLE);
}
@Test
public void bindView_shouldSetSummary() {
final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
assertThat(((DeferredSetupCardViewHolder) viewHolder).summary.getText()).isEqualTo(SUMMARY);
}
private RecyclerView.ViewHolder getDeferredSetupCardViewHolder() {
final RecyclerView recyclerView = new RecyclerView(mActivity);
recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
final View view = LayoutInflater.from(mActivity).inflate(VIEW_TYPE_DEFERRED_SETUP,
recyclerView, false);
return mHelper.createViewHolder(view);
}
private ContextualCard buildContextualCard() {
return new ContextualCard.Builder()
.setName("test_name")
.setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(TEST_SLICE_URI)
.setViewType(VIEW_TYPE_DEFERRED_SETUP)
.build();
}
private Slice buildSlice() {
final IconCompat icon = IconCompat.createWithResource(mActivity, R.drawable.empty_icon);
final PendingIntent pendingIntent = PendingIntent.getActivity(
mActivity,
TITLE.hashCode() /* requestCode */,
new Intent("test action"),
0 /* flags */);
final SliceAction action
= SliceAction.createDeeplink(pendingIntent, icon, ListBuilder.SMALL_IMAGE, TITLE);
return new ListBuilder(mActivity, TEST_SLICE_URI, ListBuilder.INFINITY)
.addRow(new ListBuilder.RowBuilder()
.addEndItem(icon, ListBuilder.ICON_IMAGE)
.setTitle(TITLE)
.setSubtitle(SUMMARY)
.setPrimaryAction(action))
.build();
}
}