Merge "Loading suggestions via legacy SuggestionService sometimes"

This commit is contained in:
Fan Zhang
2018-11-08 20:35:06 +00:00
committed by Android (Google) Code Review
21 changed files with 675 additions and 36 deletions

View File

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

View File

@@ -32,8 +32,8 @@
<ImageView <ImageView
android:id="@android:id/icon" android:id="@android:id/icon"
android:layout_width="@dimen/suggestion_card_icon_size" android:layout_width="@dimen/homepage_card_icon_size"
android:layout_height="@dimen/suggestion_card_icon_size" android:layout_height="@dimen/homepage_card_icon_size"
android:tint="?android:attr/colorAccent"/> android:tint="?android:attr/colorAccent"/>
<TextView <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 --> have distinct intensity levels -->
<bool name="config_vibration_supports_multiple_intensities">false</bool> <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 --> <!-- Whether or not TopLevelSettings should force rounded icon for injected tiles -->
<bool name="config_force_rounded_icon_TopLevelSettings">true</bool> <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="suggestion_condition_header_padding_expanded">5dp</dimen>
<dimen name="condition_header_height">36dp</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 --> <!-- Condition cards size and padding -->
<dimen name="condition_card_elevation">2dp</dimen> <dimen name="condition_card_elevation">2dp</dimen>
@@ -331,6 +320,7 @@
<dimen name="homepage_bottombar_fab_cradle">68dp</dimen> <dimen name="homepage_bottombar_fab_cradle">68dp</dimen>
<!-- Homepage cards size and padding --> <!-- 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_corner_radius">8dp</dimen>
<dimen name="homepage_card_elevation">2dp</dimen> <dimen name="homepage_card_elevation">2dp</dimen>
<dimen name="homepage_card_vertical_margin">4dp</dimen> <dimen name="homepage_card_vertical_margin">4dp</dimen>

View File

@@ -350,16 +350,8 @@
<style name="SuggestionCardIcon"> <style name="SuggestionCardIcon">
<item name="android:layout_centerHorizontal">false</item> <item name="android:layout_centerHorizontal">false</item>
<item name="android:layout_alignParentStart">true</item> <item name="android:layout_alignParentStart">true</item>
<item name="android:layout_marginStart">@dimen/suggestion_card_icon_side_margin</item> <item name="android:layout_marginStart">12dp</item>
<item name="android:layout_marginEnd">@dimen/suggestion_card_icon_side_margin</item> <item name="android:layout_marginEnd">12dp</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>
</style> </style>
<style name="FingerprintLayoutTheme"> <style name="FingerprintLayoutTheme">

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,6 +16,8 @@
package com.android.settings.homepage.contextualcards; package com.android.settings.homepage.contextualcards;
import androidx.annotation.MainThread;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -31,5 +33,6 @@ public interface ContextualCardUpdateListener {
* null, which means all cards from corresponding {@link * null, which means all cards from corresponding {@link
* ContextualCard.CardType} are removed. * ContextualCard.CardType} are removed.
*/ */
@MainThread
void onContextualCardUpdated(Map<Integer, List<ContextualCard>> cards); 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.internal.annotations.VisibleForTesting;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController; import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardController;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer; 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.SliceContextualCardController;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer; import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -111,6 +115,8 @@ public class ControllerRendererPool {
return new ConditionContextualCardController(context); return new ConditionContextualCardController(context);
} else if (SliceContextualCardController.class == clz) { } else if (SliceContextualCardController.class == clz) {
return new SliceContextualCardController(); return new SliceContextualCardController();
} else if (LegacySuggestionContextualCardController.class == clz) {
return new LegacySuggestionContextualCardController(context);
} }
return null; return null;
} }
@@ -121,6 +127,9 @@ public class ControllerRendererPool {
return new ConditionContextualCardRenderer(context, this /* controllerRendererPool */); return new ConditionContextualCardRenderer(context, this /* controllerRendererPool */);
} else if (SliceContextualCardRenderer.class == clz) { } else if (SliceContextualCardRenderer.class == clz) {
return new SliceContextualCardRenderer(context, lifecycleOwner); return new SliceContextualCardRenderer(context, lifecycleOwner);
} else if (LegacySuggestionContextualCardRenderer.class == clz) {
return new LegacySuggestionContextualCardRenderer(context,
this /* controllerRendererPool */);
} }
return null; return null;
} }

View File

@@ -23,6 +23,7 @@ import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.LayoutRes;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
@@ -37,7 +38,9 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
* Card renderer for {@link ConditionalContextualCard}. * Card renderer for {@link ConditionalContextualCard}.
*/ */
public class ConditionContextualCardRenderer implements ContextualCardRenderer { public class ConditionContextualCardRenderer implements ContextualCardRenderer {
@LayoutRes
public static final int HALF_WIDTH_VIEW_TYPE = R.layout.homepage_condition_half_tile; 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; public static final int FULL_WIDTH_VIEW_TYPE = R.layout.homepage_condition_full_tile;
private final Context mContext; 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_show_wifi_mac_address">false</bool>
<bool name="config_disable_uninstall_update">true</bool> <bool name="config_disable_uninstall_update">true</bool>
<bool name="config_show_device_name">false</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. --> <!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">false</bool> <bool name="config_enable_extra_screen_zoom_preview">false</bool>

View File

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