Loading suggestions via legacy SuggestionService sometimes

When legacy_suggestion flag is turned on, load Suggestions from
SuggestionService instead of contextual homepage, because contextual
homepage infrastructure might not be availble on all devices.

Bug: 118842099
Test: robo
Change-Id: I91710c005e11be5a9b3dd39ceff670106e7f80c3
This commit is contained in:
Fan Zhang
2018-11-05 15:40:32 -08:00
parent 16c2d70881
commit 2a98c843e8
21 changed files with 675 additions and 36 deletions

View File

@@ -32,8 +32,8 @@
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/suggestion_card_icon_size"
android:layout_height="@dimen/suggestion_card_icon_size"
android:layout_width="@dimen/homepage_card_icon_size"
android:layout_height="@dimen/homepage_card_icon_size"
android:tint="?android:attr/colorAccent"/>
<LinearLayout

View File

@@ -32,8 +32,8 @@
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/suggestion_card_icon_size"
android:layout_height="@dimen/suggestion_card_icon_size"
android:layout_width="@dimen/homepage_card_icon_size"
android:layout_height="@dimen/homepage_card_icon_size"
android:tint="?android:attr/colorAccent"/>
<TextView

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/suggestion_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/ContextualCardStyle">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="112dp"
android:paddingBottom="8dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@android:id/icon"
android:layout_width="@dimen/homepage_card_icon_size"
android:layout_height="@dimen/homepage_card_icon_size"
style="@style/SuggestionCardIcon"
android:layout_marginTop="16dp"
android:layout_marginBottom="6dp"/>
</RelativeLayout>
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/SuggestionCardText"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.SuggestionTitle"
android:fadingEdge="horizontal"/>
<TextView
android:id="@android:id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/SuggestionCardText"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.SuggestionSummary"/>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -134,6 +134,12 @@
have distinct intensity levels -->
<bool name="config_vibration_supports_multiple_intensities">false</bool>
<!--
Whether or not the homepage should be powered by legacy suggestion (versus contextual cards)
Default to true as not all devices support contextual cards.
-->
<bool name="config_use_legacy_suggestion">true</bool>
<!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool>

View File

@@ -303,17 +303,6 @@
<dimen name="suggestion_condition_header_padding_expanded">5dp</dimen>
<dimen name="condition_header_height">36dp</dimen>
<!-- Suggestion cards size and padding -->
<dimen name="suggestion_card_icon_size">24dp</dimen>
<dimen name="suggestion_card_outer_margin">14dp</dimen>
<dimen name="suggestion_card_inner_margin">12dp</dimen>
<dimen name="suggestion_card_padding_bottom_one_card">16dp</dimen>
<dimen name="suggestion_card_corner_radius">2dp</dimen>
<dimen name="suggestion_card_icon_side_margin">12dp</dimen>
<dimen name="suggestion_card_button_side_margin">8dp</dimen>
<dimen name="suggestion_card_button_top_margin">16dp</dimen>
<dimen name="suggestion_card_button_bottom_margin">18dp</dimen>
<!-- Condition cards size and padding -->
<dimen name="condition_card_elevation">2dp</dimen>
@@ -331,6 +320,7 @@
<dimen name="homepage_bottombar_fab_cradle">68dp</dimen>
<!-- Homepage cards size and padding -->
<dimen name="homepage_card_icon_size">24dp</dimen>
<dimen name="homepage_card_corner_radius">8dp</dimen>
<dimen name="homepage_card_elevation">2dp</dimen>
<dimen name="homepage_card_vertical_margin">4dp</dimen>

View File

@@ -350,16 +350,8 @@
<style name="SuggestionCardIcon">
<item name="android:layout_centerHorizontal">false</item>
<item name="android:layout_alignParentStart">true</item>
<item name="android:layout_marginStart">@dimen/suggestion_card_icon_side_margin</item>
<item name="android:layout_marginEnd">@dimen/suggestion_card_icon_side_margin</item>
</style>
<style name="SuggestionCardButton">
<item name="android:layout_gravity">start</item>
<item name="android:layout_marginStart">@dimen/suggestion_card_button_side_margin</item>
<item name="android:layout_marginEnd">@dimen/suggestion_card_button_side_margin</item>
<item name="android:layout_marginTop">@dimen/suggestion_card_button_top_margin</item>
<item name="android:layout_marginBottom">@dimen/suggestion_card_button_bottom_margin</item>
<item name="android:layout_marginStart">12dp</item>
<item name="android:layout_marginEnd">12dp</item>
</style>
<style name="FingerprintLayoutTheme">

View File

@@ -33,12 +33,12 @@ public class ContextualCard {
/**
* Flags indicating the type of the ContextualCard.
*/
@IntDef({CardType.DEFAULT, CardType.SLICE, CardType.SUGGESTION, CardType.CONDITIONAL})
@IntDef({CardType.DEFAULT, CardType.SLICE, CardType.LEGACY_SUGGESTION, CardType.CONDITIONAL})
@Retention(RetentionPolicy.SOURCE)
public @interface CardType {
int DEFAULT = 0;
int SLICE = 1;
int SUGGESTION = 2;
int LEGACY_SUGGESTION = 2;
int CONDITIONAL = 3;
}

View File

@@ -18,11 +18,16 @@ package com.android.settings.homepage.contextualcards;
import android.util.Log;
import androidx.annotation.LayoutRes;
import androidx.annotation.VisibleForTesting;
import com.android.settings.homepage.contextualcards.ContextualCard.CardType;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -34,6 +39,7 @@ import java.util.stream.Collectors;
public class ContextualCardLookupTable {
private static final String TAG = "ContextualCardLookup";
static class ControllerRendererMapping implements Comparable<ControllerRendererMapping> {
@CardType
final int mCardType;
@@ -41,7 +47,7 @@ public class ContextualCardLookupTable {
final Class<? extends ContextualCardController> mControllerClass;
final Class<? extends ContextualCardRenderer> mRendererClass;
ControllerRendererMapping(@CardType int cardType, int viewType,
ControllerRendererMapping(@CardType int cardType, @LayoutRes int viewType,
Class<? extends ContextualCardController> controllerClass,
Class<? extends ContextualCardRenderer> rendererClass) {
mCardType = cardType;
@@ -69,6 +75,10 @@ public class ContextualCardLookupTable {
ConditionContextualCardRenderer.FULL_WIDTH_VIEW_TYPE,
ConditionContextualCardController.class,
ConditionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.LEGACY_SUGGESTION,
LegacySuggestionContextualCardRenderer.VIEW_TYPE,
LegacySuggestionContextualCardController.class,
LegacySuggestionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.SLICE,
SliceContextualCardRenderer.VIEW_TYPE,
SliceContextualCardController.class,

View File

@@ -16,7 +16,8 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader
.CARD_CONTENT_LOADER_ID;
import static java.util.stream.Collectors.groupingBy;
@@ -57,8 +58,8 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private static final String TAG = "ContextualCardManager";
//The list for Settings Custom Card
@ContextualCard.CardType
private static final int[] SETTINGS_CARDS = {ContextualCard.CardType.CONDITIONAL};
private static final int[] SETTINGS_CARDS =
{ContextualCard.CardType.CONDITIONAL, ContextualCard.CardType.LEGACY_SUGGESTION};
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
@@ -68,14 +69,14 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
private ContextualCardUpdateListener mListener;
public ContextualCardManager(Context context, @NonNull Lifecycle lifecycle) {
public ContextualCardManager(Context context, Lifecycle lifecycle) {
mContext = context;
mLifecycle = lifecycle;
mContextualCards = new ArrayList<>();
mLifecycleObservers = new ArrayList<>();
mControllerRendererPool = new ControllerRendererPool();
//for data provided by Settings
for (int cardType : SETTINGS_CARDS) {
for (@ContextualCard.CardType int cardType : SETTINGS_CARDS) {
setupController(cardType);
}
}
@@ -94,7 +95,7 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
}
}
private void setupController(int cardType) {
private void setupController(@ContextualCard.CardType int cardType) {
final ContextualCardController controller = mControllerRendererPool.getController(mContext,
cardType);
if (controller == null) {

View File

@@ -18,6 +18,7 @@ package com.android.settings.homepage.contextualcards;
import android.view.View;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
/**
@@ -28,6 +29,7 @@ public interface ContextualCardRenderer {
/**
* The layout type of the renderer.
*/
@LayoutRes
int getViewType(boolean isHalfWidth);
/**

View File

@@ -16,6 +16,8 @@
package com.android.settings.homepage.contextualcards;
import androidx.annotation.MainThread;
import java.util.List;
import java.util.Map;
@@ -31,5 +33,6 @@ public interface ContextualCardUpdateListener {
* null, which means all cards from corresponding {@link
* ContextualCard.CardType} are removed.
*/
@MainThread
void onContextualCardUpdated(Map<Integer, List<ContextualCard>> cards);
}

View File

@@ -26,6 +26,10 @@ import androidx.lifecycle.LifecycleOwner;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardController;
import com.android.settings.homepage.contextualcards.legacysuggestion
.LegacySuggestionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -111,6 +115,8 @@ public class ControllerRendererPool {
return new ConditionContextualCardController(context);
} else if (SliceContextualCardController.class == clz) {
return new SliceContextualCardController();
} else if (LegacySuggestionContextualCardController.class == clz) {
return new LegacySuggestionContextualCardController(context);
}
return null;
}
@@ -118,9 +124,12 @@ public class ControllerRendererPool {
private ContextualCardRenderer createCardRenderer(Context context,
LifecycleOwner lifecycleOwner, Class<?> clz) {
if (ConditionContextualCardRenderer.class == clz) {
return new ConditionContextualCardRenderer(context, this /*controllerRendererPool*/);
return new ConditionContextualCardRenderer(context, this /* controllerRendererPool */);
} else if (SliceContextualCardRenderer.class == clz) {
return new SliceContextualCardRenderer(context, lifecycleOwner);
} else if (LegacySuggestionContextualCardRenderer.class == clz) {
return new LegacySuggestionContextualCardRenderer(context,
this /* controllerRendererPool */);
}
return null;
}

View File

@@ -23,6 +23,7 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto;
@@ -37,7 +38,9 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
* Card renderer for {@link ConditionalContextualCard}.
*/
public class ConditionContextualCardRenderer implements ContextualCardRenderer {
@LayoutRes
public static final int HALF_WIDTH_VIEW_TYPE = R.layout.homepage_condition_half_tile;
@LayoutRes
public static final int FULL_WIDTH_VIEW_TYPE = R.layout.homepage_condition_full_tile;
private final Context mContext;

View File

@@ -0,0 +1,61 @@
/*
* 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.legacysuggestion;
import android.app.PendingIntent;
import com.android.settings.homepage.contextualcards.ContextualCard;
public class LegacySuggestionContextualCard extends ContextualCard {
private final PendingIntent mPendingIntent;
public LegacySuggestionContextualCard(Builder builder) {
super(builder);
mPendingIntent = builder.mPendingIntent;
}
@Override
public int getCardType() {
return CardType.LEGACY_SUGGESTION;
}
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
public static class Builder extends ContextualCard.Builder {
private PendingIntent mPendingIntent;
public Builder setPendingIntent(PendingIntent pendingIntent) {
mPendingIntent = pendingIntent;
return this;
}
@Override
public Builder setCardType(int cardType) {
throw new IllegalArgumentException(
"Cannot change card type for " + getClass().getName());
}
public LegacySuggestionContextualCard build() {
return new LegacySuggestionContextualCard(this);
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.legacysuggestion;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardController;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.suggestions.SuggestionController;
import com.android.settingslib.suggestions.SuggestionController.ServiceConnectionListener;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class LegacySuggestionContextualCardController implements ContextualCardController,
LifecycleObserver, OnStart, OnStop, ServiceConnectionListener {
private static final String TAG = "LegacySuggestCardCtrl";
@VisibleForTesting
SuggestionController mSuggestionController;
private ContextualCardUpdateListener mCardUpdateListener;
private final Context mContext;
public LegacySuggestionContextualCardController(Context context) {
mContext = context;
if (!mContext.getResources().getBoolean(R.bool.config_use_legacy_suggestion)) {
Log.w(TAG, "Legacy suggestion contextual card disabled, skipping.");
return;
}
final ComponentName suggestionServiceComponent =
FeatureFactory.getFactory(mContext).getSuggestionFeatureProvider(mContext)
.getSuggestionServiceComponent();
mSuggestionController = new SuggestionController(
mContext, suggestionServiceComponent, this /* listener */);
}
@Override
public int getCardType() {
return ContextualCard.CardType.LEGACY_SUGGESTION;
}
@Override
public void onPrimaryClick(ContextualCard card) {
try {
((LegacySuggestionContextualCard) card).getPendingIntent().send();
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Failed to start suggestion " + card.getTitleText());
}
}
@Override
public void onActionClick(ContextualCard card) {
}
@Override
public void setCardUpdateListener(ContextualCardUpdateListener listener) {
mCardUpdateListener = listener;
}
@Override
public void onStart() {
if (mSuggestionController == null) {
return;
}
mSuggestionController.start();
}
@Override
public void onStop() {
if (mSuggestionController == null) {
return;
}
mSuggestionController.stop();
}
@Override
public void onServiceConnected() {
loadSuggestions();
}
@Override
public void onServiceDisconnected() {
}
private void loadSuggestions() {
ThreadUtils.postOnBackgroundThread(() -> {
if (mSuggestionController == null || mCardUpdateListener == null) {
return;
}
final List<Suggestion> suggestions = mSuggestionController.getSuggestions();
Log.d(TAG, "Loaded suggests: "
+ suggestions == null ? "null" : String.valueOf(suggestions.size()));
final List<ContextualCard> cards = new ArrayList<>();
if (suggestions != null) {
// Convert suggestion to ContextualCard
for (Suggestion suggestion : suggestions) {
final LegacySuggestionContextualCard.Builder cardBuilder =
new LegacySuggestionContextualCard.Builder();
if (suggestion.getIcon() != null) {
cardBuilder.setIconDrawable(suggestion.getIcon().loadDrawable(mContext));
}
cardBuilder
.setPendingIntent(suggestion.getPendingIntent())
.setName(suggestion.getId())
.setTitleText(suggestion.getTitle().toString())
.setSummaryText(suggestion.getSummary().toString());
cards.add(cardBuilder.build());
}
}
// Update adapter
final Map<Integer, List<ContextualCard>> suggestionCards = new ArrayMap<>();
suggestionCards.put(ContextualCard.CardType.LEGACY_SUGGESTION, cards);
ThreadUtils.postOnMainThread(
() -> mCardUpdateListener.onContextualCardUpdated(suggestionCards));
});
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.legacysuggestion;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
import com.android.settings.homepage.contextualcards.ControllerRendererPool;
public class LegacySuggestionContextualCardRenderer implements ContextualCardRenderer {
@LayoutRes
public static final int VIEW_TYPE = R.layout.homepage_suggestion_tile;
private final Context mContext;
private final ControllerRendererPool mControllerRendererPool;
public LegacySuggestionContextualCardRenderer(Context context,
ControllerRendererPool controllerRendererPool) {
mContext = context;
mControllerRendererPool = controllerRendererPool;
}
@Override
public int getViewType(boolean isHalfWidth) {
return VIEW_TYPE;
}
@Override
public RecyclerView.ViewHolder createViewHolder(View view) {
return new LegacySuggestionViewHolder(view);
}
@Override
public void bindView(RecyclerView.ViewHolder holder, ContextualCard card) {
final LegacySuggestionViewHolder vh = (LegacySuggestionViewHolder) holder;
vh.icon.setImageDrawable(card.getIconDrawable());
vh.title.setText(card.getTitleText());
vh.summary.setText(card.getSummaryText());
vh.itemView.setOnClickListener(v ->
mControllerRendererPool.getController(mContext,
card.getCardType()).onPrimaryClick(card));
}
private static class LegacySuggestionViewHolder extends RecyclerView.ViewHolder {
public final ImageView icon;
public final TextView title;
public final TextView summary;
public LegacySuggestionViewHolder(View itemView) {
super(itemView);
icon = itemView.findViewById(android.R.id.icon);
title = itemView.findViewById(android.R.id.title);
summary = itemView.findViewById(android.R.id.summary);
}
}
}

View File

@@ -62,6 +62,7 @@
<bool name="config_show_wifi_mac_address">false</bool>
<bool name="config_disable_uninstall_update">true</bool>
<bool name="config_show_device_name">false</bool>
<bool name="config_use_legacy_suggestion">false</bool>
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">false</bool>

View File

@@ -30,7 +30,7 @@ public class ConditionalContextualCardTest {
@Test(expected = IllegalArgumentException.class)
public void newInstance_changeCardType_shouldCrash() {
new ConditionalContextualCard.Builder()
.setCardType(ContextualCard.CardType.SUGGESTION)
.setCardType(ContextualCard.CardType.LEGACY_SUGGESTION)
.build();
}

View File

@@ -0,0 +1,94 @@
/*
* 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.legacysuggestion;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import android.content.Context;
import com.android.settings.homepage.contextualcards.ContextualCardUpdateListener;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.suggestions.SuggestionController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowThreadUtils.class)
public class LegacySuggestionContextualCardControllerTest {
@Mock
private SuggestionController mSuggestionController;
@Mock
private ContextualCardUpdateListener mCardUpdateListener;
private Context mContext;
private LegacySuggestionContextualCardController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
mContext = RuntimeEnvironment.application;
mController = new LegacySuggestionContextualCardController(mContext);
}
@Test
public void init_configOn_shouldCreateSuggestionController() {
final LegacySuggestionContextualCardController controller =
new LegacySuggestionContextualCardController(mContext);
assertThat(controller.mSuggestionController).isNotNull();
}
@Test
@Config(qualifiers = "mcc999")
public void init_configOff_shouldNotCreateSuggestionController() {
final LegacySuggestionContextualCardController controller =
new LegacySuggestionContextualCardController(mContext);
assertThat(controller.mSuggestionController).isNull();
}
@Test
public void goThroughLifecycle_hasSuggestionController_shouldStartStopController() {
mController.mSuggestionController = mSuggestionController;
mController.onStart();
verify(mSuggestionController).start();
mController.onStop();
verify(mSuggestionController).stop();
}
@Test
public void onServiceConnected_shouldLoadSuggestion() {
mController.mSuggestionController = mSuggestionController;
mController.setCardUpdateListener(mCardUpdateListener);
mController.onServiceConnected();
verify(mSuggestionController).getSuggestions();
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.legacysuggestion;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ControllerRendererPool;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class LegacySuggestionContextualCardRendererTest {
@Mock
private ControllerRendererPool mControllerRendererPool;
@Mock
private LegacySuggestionContextualCardController mController;
private Context mContext;
private LegacySuggestionContextualCardRenderer mRenderer;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mRenderer = new LegacySuggestionContextualCardRenderer(mContext, mControllerRendererPool);
}
@Test
public void bindView_shouldSetListener() {
final int viewType = mRenderer.getViewType(true /* isHalfWidth */);
final RecyclerView recyclerView = new RecyclerView(mContext);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
final View card = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(card);
when(mControllerRendererPool.getController(mContext,
ContextualCard.CardType.LEGACY_SUGGESTION)).thenReturn(mController);
mRenderer.bindView(viewHolder, buildContextualCard());
assertThat(card).isNotNull();
assertThat(card.hasOnClickListeners()).isTrue();
}
@Test
public void viewClick_shouldInvokeControllerPrimaryClick() {
final int viewType = mRenderer.getViewType(true /* isHalfWidth */);
final RecyclerView recyclerView = new RecyclerView(mContext);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
final View card = LayoutInflater.from(mContext).inflate(viewType, recyclerView, false);
final RecyclerView.ViewHolder viewHolder = mRenderer.createViewHolder(card);
when(mControllerRendererPool.getController(mContext,
ContextualCard.CardType.LEGACY_SUGGESTION)).thenReturn(mController);
mRenderer.bindView(viewHolder, buildContextualCard());
assertThat(card).isNotNull();
card.performClick();
verify(mController).onPrimaryClick(any(ContextualCard.class));
}
private ContextualCard buildContextualCard() {
return new LegacySuggestionContextualCard.Builder()
.setName("test_name")
.setTitleText("test_title")
.setSummaryText("test_summary")
.setIconDrawable(mContext.getDrawable(R.drawable.ic_do_not_disturb_on_24dp))
.build();
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.legacysuggestion;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.app.PendingIntent;
import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(SettingsRobolectricTestRunner.class)
public class LegacySuggestionContextualCardTest {
@Test(expected = IllegalArgumentException.class)
public void newInstance_changeCardType_shouldCrash() {
new LegacySuggestionContextualCard.Builder()
.setCardType(ContextualCard.CardType.CONDITIONAL)
.build();
}
@Test
public void getCardType_shouldAlwaysBeSuggestionType() {
assertThat(new LegacySuggestionContextualCard.Builder().build().getCardType())
.isEqualTo(ContextualCard.CardType.LEGACY_SUGGESTION);
}
@Test
public void build_shouldSetPendingIntent() {
assertThat(new LegacySuggestionContextualCard.Builder()
.setPendingIntent(mock(PendingIntent.class))
.build()
.getPendingIntent()).isNotNull();
}
}